Switch associated_type_shorthand_candidates to lower_nextsolver

This commit is contained in:
jackh726 2025-08-11 05:19:44 +00:00
parent 496f5f9e96
commit f9d2d2dd87
9 changed files with 197 additions and 159 deletions

View File

@ -564,7 +564,7 @@ fn receiver_is_dispatchable<'db>(
// U: Trait<Arg1, ..., ArgN>
let trait_def_id = SolverDefId::TraitId(trait_);
let args = GenericArgs::for_item(interner, trait_def_id, |name, index, kind, _| {
if index == 0 { unsized_self_ty.into() } else { mk_param(index, name, kind) }
if index == 0 { unsized_self_ty.into() } else { mk_param(interner, index, name, kind) }
});
let trait_predicate =
crate::next_solver::TraitRef::new_from_args(interner, trait_def_id, args);
@ -611,7 +611,7 @@ fn receiver_for_self_ty<'db>(
interner,
SolverDefId::FunctionId(func),
|name, index, kind, _| {
if index == 0 { self_ty.into() } else { mk_param(index, name, kind) }
if index == 0 { self_ty.into() } else { mk_param(interner, index, name, kind) }
},
);

View File

@ -118,8 +118,9 @@ pub use infer::{
pub use interner::Interner;
pub use lower::{
ImplTraitLoweringMode, LifetimeElisionKind, ParamLoweringMode, TyDefId, TyLoweringContext,
ValueTyDefId, associated_type_shorthand_candidates, diagnostics::*,
ValueTyDefId, diagnostics::*,
};
pub use lower_nextsolver::associated_type_shorthand_candidates;
pub use mapping::{
ToChalk, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
lt_from_placeholder_idx, lt_to_placeholder_idx, to_assoc_type_id, to_chalk_trait_id,

View File

@ -804,15 +804,6 @@ pub(crate) fn callable_item_signature_query(db: &dyn HirDatabase, def: CallableD
}
}
pub fn associated_type_shorthand_candidates<R>(
db: &dyn HirDatabase,
def: GenericDefId,
res: TypeNs,
mut cb: impl FnMut(&Name, TypeAliasId) -> Option<R>,
) -> Option<R> {
named_associated_type_shorthand_candidates(db, def, res, None, |name, _, id| cb(name, id))
}
fn named_associated_type_shorthand_candidates<R>(
db: &dyn HirDatabase,
// If the type parameter is defined in an impl and we're in a method, there

View File

@ -12,14 +12,14 @@ pub(crate) mod path;
use std::{
cell::OnceCell,
iter, mem,
ops::{self, Not as _},
ops::{self, Deref, Not as _},
};
use base_db::Crate;
use either::Either;
use hir_def::{
AdtId, AssocItemId, CallableDefId, ConstParamId, EnumVariantId, FunctionId, GenericDefId,
GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StructId, TypeAliasId,
GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StructId, TraitId, TypeAliasId,
TypeOrConstParamId, VariantId,
expr_store::{
ExpressionStore,
@ -49,6 +49,7 @@ use rustc_type_ir::{
inherent::{GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, Ty as _},
};
use salsa::plumbing::AsId;
use smallvec::{SmallVec, smallvec};
use stdx::never;
use triomphe::Arc;
@ -1607,3 +1608,131 @@ pub(crate) fn associated_type_by_name_including_super_traits<'db>(
Some((t.skip_binder(), assoc_type))
})
}
pub fn associated_type_shorthand_candidates(
db: &dyn HirDatabase,
def: GenericDefId,
res: TypeNs,
mut cb: impl FnMut(&Name, TypeAliasId) -> bool,
) -> Option<TypeAliasId> {
let interner = DbInterner::new_with(db, None, None);
named_associated_type_shorthand_candidates(interner, def, res, None, |name, _, id| {
cb(name, id).then_some(id)
})
}
#[tracing::instrument(skip(interner, check_alias))]
fn named_associated_type_shorthand_candidates<'db, R>(
interner: DbInterner<'db>,
// If the type parameter is defined in an impl and we're in a method, there
// might be additional where clauses to consider
def: GenericDefId,
res: TypeNs,
assoc_name: Option<Name>,
mut check_alias: impl FnMut(&Name, TraitRef<'db>, TypeAliasId) -> Option<R>,
) -> Option<R> {
let db = interner.db;
let mut search = |t: TraitRef<'db>| -> Option<R> {
let trait_id = match t.def_id {
SolverDefId::TraitId(id) => id,
_ => unreachable!(),
};
let mut checked_traits = FxHashSet::default();
let mut check_trait = |trait_id: TraitId| {
let name = &db.trait_signature(trait_id).name;
tracing::debug!(?trait_id, ?name);
if !checked_traits.insert(trait_id) {
return None;
}
let data = trait_id.trait_items(db);
tracing::debug!(?data.items);
for (name, assoc_id) in &data.items {
if let &AssocItemId::TypeAliasId(alias) = assoc_id
&& let Some(ty) = check_alias(name, t, alias)
{
return Some(ty);
}
}
None
};
let mut stack: SmallVec<[_; 4]> = smallvec![trait_id];
while let Some(trait_def_id) = stack.pop() {
if let Some(alias) = check_trait(trait_def_id) {
return Some(alias);
}
for pred in generic_predicates_filtered_by(
db,
GenericDefId::TraitId(trait_def_id),
PredicateFilter::SelfTrait,
|pred| pred == GenericDefId::TraitId(trait_def_id),
)
.0
.deref()
{
tracing::debug!(?pred);
let trait_id = match pred.kind().skip_binder() {
rustc_type_ir::ClauseKind::Trait(pred) => pred.def_id(),
_ => continue,
};
let trait_id = match trait_id {
SolverDefId::TraitId(trait_id) => trait_id,
_ => continue,
};
stack.push(trait_id);
}
tracing::debug!(?stack);
}
None
};
match res {
TypeNs::SelfType(impl_id) => {
let trait_ref = db.impl_trait_ns(impl_id)?;
// we're _in_ the impl -- the binders get added back later. Correct,
// but it would be nice to make this more explicit
search(trait_ref.skip_binder())
}
TypeNs::GenericParam(param_id) => {
// Handle `Self::Type` referring to own associated type in trait definitions
// This *must* be done first to avoid cycles with
// `generic_predicates_for_param`, but not sure that it's sufficient,
// see FIXME in `search`.
if let GenericDefId::TraitId(trait_id) = param_id.parent() {
let trait_name = &db.trait_signature(trait_id).name;
tracing::debug!(?trait_name);
let trait_generics = generics(db, trait_id.into());
tracing::debug!(?trait_generics);
if trait_generics[param_id.local_id()].is_trait_self() {
let args = crate::next_solver::GenericArgs::identity_for_item(
interner,
trait_id.into(),
);
let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args);
tracing::debug!(?args, ?trait_ref);
return search(trait_ref);
}
}
let predicates =
db.generic_predicates_for_param_ns(def, param_id.into(), assoc_name.clone());
predicates
.iter()
.find_map(|pred| match (*pred).kind().skip_binder() {
rustc_type_ir::ClauseKind::Trait(trait_predicate) => Some(trait_predicate),
_ => None,
})
.and_then(|trait_predicate| {
let trait_ref = trait_predicate.trait_ref;
assert!(
!trait_ref.has_escaping_bound_vars(),
"FIXME unexpected higher-ranked trait bound"
);
search(trait_ref)
})
}
_ => None,
}
}

View File

@ -4,7 +4,7 @@ use std::ops::Deref;
use either::Either;
use hir_def::{
AssocItemId, GenericDefId, GenericParamId, Lookup, TraitId,
AssocItemId, GenericDefId, GenericParamId, Lookup, TraitId, TypeAliasId,
builtin_type::BuiltinType,
expr_store::{
ExpressionStore, HygieneId,
@ -17,6 +17,7 @@ use hir_def::{
signatures::TraitFlags,
type_ref::{TypeRef, TypeRefId},
};
use hir_expand::name::Name;
use intern::sym;
use rustc_hash::FxHashSet;
use rustc_type_ir::{
@ -33,7 +34,10 @@ use crate::{
db::HirDatabase,
generics::{Generics, generics},
lower::PathDiagnosticCallbackData,
lower_nextsolver::{LifetimeElisionKind, PredicateFilter, generic_predicates_filtered_by},
lower_nextsolver::{
LifetimeElisionKind, PredicateFilter, generic_predicates_filtered_by,
named_associated_type_shorthand_candidates,
},
next_solver::{
AdtDef, Binder, Clause, Const, DbInterner, ErrorGuaranteed, Predicate, ProjectionPredicate,
Region, SolverDefId, TraitRef, Ty,
@ -501,137 +505,40 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
let Some(res) = res else {
return Ty::new_error(self.ctx.interner, ErrorGuaranteed);
};
let segment = self.current_or_prev_segment;
let assoc_name = segment.name;
let db = self.ctx.db;
let def = self.ctx.def;
let mut search = |t: TraitRef<'db>| {
let trait_id = match t.def_id {
SolverDefId::TraitId(id) => id,
_ => unreachable!(),
};
let mut checked_traits = FxHashSet::default();
let mut check_trait = |trait_id: TraitId| {
let name = &db.trait_signature(trait_id).name;
tracing::debug!(?trait_id, ?name);
if !checked_traits.insert(trait_id) {
return None;
}
let data = trait_id.trait_items(db);
tracing::debug!(?data.items);
for (name, assoc_id) in &data.items {
if let &AssocItemId::TypeAliasId(alias) = assoc_id {
if name != assoc_name {
continue;
}
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
// generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at
// this point (`t.substitution`).
let substs = self.substs_from_path_segment(alias.into(), false, None, true);
let substs = crate::next_solver::GenericArgs::new_from_iter(
interner,
t.args.iter().chain(substs.iter().skip(t.args.len())),
);
return Some(Ty::new_alias(
interner,
AliasTyKind::Projection,
AliasTy::new(interner, alias.into(), substs),
));
}
}
None
};
let mut stack: SmallVec<[_; 4]> = smallvec![trait_id];
while let Some(trait_def_id) = stack.pop() {
if let Some(alias) = check_trait(trait_def_id) {
return alias;
}
for pred in generic_predicates_filtered_by(
db,
GenericDefId::TraitId(trait_def_id),
PredicateFilter::SelfTrait,
|pred| pred == GenericDefId::TraitId(trait_def_id),
)
.0
.deref()
{
tracing::debug!(?pred);
let trait_id = match pred.kind().skip_binder() {
rustc_type_ir::ClauseKind::Trait(pred) => pred.def_id(),
_ => continue,
};
let trait_id = match trait_id {
SolverDefId::TraitId(trait_id) => trait_id,
_ => continue,
};
stack.push(trait_id);
}
tracing::debug!(?stack);
let segment = self.current_or_prev_segment;
let assoc_name = segment.name;
let mut check_alias = |name: &Name, t: TraitRef<'db>, associated_ty: TypeAliasId| {
if name != assoc_name {
return None;
}
Ty::new_error(interner, ErrorGuaranteed)
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
// generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at
// this point (`t.substitution`).
let substs = self.substs_from_path_segment(associated_ty.into(), false, None, true);
let substs = crate::next_solver::GenericArgs::new_from_iter(
interner,
t.args.iter().chain(substs.iter().skip(t.args.len())),
);
Some(Ty::new_alias(
interner,
AliasTyKind::Projection,
AliasTy::new(interner, associated_ty.into(), substs),
))
};
match res {
TypeNs::SelfType(impl_id) => {
let trait_ref = db.impl_trait_ns(impl_id);
let Some(trait_ref) = trait_ref else {
return Ty::new_error(interner, ErrorGuaranteed);
};
// we're _in_ the impl -- the binders get added back later. Correct,
// but it would be nice to make this more explicit
search(trait_ref.skip_binder())
}
TypeNs::GenericParam(param_id) => {
// Handle `Self::Type` referring to own associated type in trait definitions
// This *must* be done first to avoid cycles with
// `generic_predicates_for_param`, but not sure that it's sufficient,
// see FIXME in `search`.
if let GenericDefId::TraitId(trait_id) = param_id.parent() {
let trait_name = &db.trait_signature(trait_id).name;
tracing::debug!(?trait_name);
let trait_generics = generics(db, trait_id.into());
tracing::debug!(?trait_generics);
if trait_generics[param_id.local_id()].is_trait_self() {
let args = crate::next_solver::GenericArgs::identity_for_item(
interner,
trait_id.into(),
);
let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args);
tracing::debug!(?args, ?trait_ref);
return search(trait_ref);
}
}
let predicates = db.generic_predicates_for_param_ns(
def,
param_id.into(),
Some(segment.name.clone()),
);
predicates
.iter()
.find_map(|pred| match (*pred).kind().skip_binder() {
rustc_type_ir::ClauseKind::Trait(trait_predicate) => Some(trait_predicate),
_ => None,
})
.map(|trait_predicate| {
let trait_ref = trait_predicate.trait_ref;
assert!(
!trait_ref.has_escaping_bound_vars(),
"FIXME unexpected higher-ranked trait bound"
);
search(trait_ref)
})
.unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed))
}
_ => Ty::new_error(interner, ErrorGuaranteed),
}
named_associated_type_shorthand_candidates(
interner,
def,
res,
Some(assoc_name.clone()),
check_alias,
)
.unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed))
}
fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> {

View File

@ -263,7 +263,9 @@ impl<'db> rustc_type_ir::inherent::GenericArgs<DbInterner<'db>> for GenericArgs<
interner: DbInterner<'db>,
def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId,
) -> <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs {
Self::for_item(interner, def_id, |name, index, kind, _| mk_param(index, name, kind))
Self::for_item(interner, def_id, |name, index, kind, _| {
mk_param(interner, index, name, kind)
})
}
fn extend_with_error(
@ -383,16 +385,19 @@ impl<'db> rustc_type_ir::inherent::GenericArgs<DbInterner<'db>> for GenericArgs<
}
}
pub fn mk_param<'db>(index: u32, name: &Symbol, kind: GenericParamDefKind) -> GenericArg<'db> {
pub fn mk_param<'db>(
interner: DbInterner<'db>,
index: u32,
name: &Symbol,
kind: GenericParamDefKind,
) -> GenericArg<'db> {
let name = name.clone();
match kind {
GenericParamDefKind::Lifetime => {
Region::new_early_param(DbInterner::conjure(), EarlyParamRegion { index }).into()
}
GenericParamDefKind::Type => Ty::new_param(DbInterner::conjure(), index, name).into(),
GenericParamDefKind::Const => {
Const::new_param(DbInterner::conjure(), ParamConst { index }).into()
Region::new_early_param(interner, EarlyParamRegion { index }).into()
}
GenericParamDefKind::Type => Ty::new_param(interner, index, name).into(),
GenericParamDefKind::Const => Const::new_param(interner, ParamConst { index }).into(),
}
}

View File

@ -2299,7 +2299,7 @@ impl<'db> SemanticsScope<'db> {
};
hir_ty::associated_type_shorthand_candidates(self.db, def, resolution, |_, id| {
cb(id.into());
None::<()>
false
});
}

View File

@ -14,6 +14,7 @@ use crate::{
db::HirDatabase,
semantics::{PathResolution, PathResolutionPerNs},
};
use base_db::salsa;
use either::Either;
use hir_def::{
AdtId, AssocItemId, CallableDefId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId,
@ -1593,12 +1594,14 @@ fn resolve_hir_path_(
Some(unresolved) => resolver
.generic_def()
.and_then(|def| {
hir_ty::associated_type_shorthand_candidates(
db,
def,
res.in_type_ns()?,
|name, id| (name == unresolved.name).then_some(id),
)
salsa::attach(db, || {
hir_ty::associated_type_shorthand_candidates(
db,
def,
res.in_type_ns()?,
|name, _| name == unresolved.name,
)
})
})
.map(TypeAlias::from)
.map(Into::into)
@ -1746,7 +1749,7 @@ fn resolve_hir_path_qualifier(
db,
def,
res.in_type_ns()?,
|name, id| (name == unresolved.name).then_some(id),
|name, _| name == unresolved.name,
)
})
.map(TypeAlias::from)

View File

@ -528,7 +528,9 @@ impl Analysis {
let search_scope = AssertUnwindSafe(search_scope);
self.with_db(|db| {
let _ = &search_scope;
references::find_all_refs(&Semantics::new(db), position, search_scope.0)
salsa::attach(db, || {
references::find_all_refs(&Semantics::new(db), position, search_scope.0)
})
})
}
@ -574,7 +576,7 @@ impl Analysis {
&self,
position: FilePosition,
) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
self.with_db(|db| call_hierarchy::call_hierarchy(db, position))
self.with_db(|db| salsa::attach(db, || call_hierarchy::call_hierarchy(db, position)))
}
/// Computes incoming calls for the given file position.