From a7f6e5312a6713e8648a55e4573810c40111fda4 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 12 Feb 2026 18:50:20 +0200 Subject: [PATCH] Refactor handling of associated type shorthand for type parameters, i.e. `T::AssocType` without specifying the trait I believe the new code is both cleaner and more robust, and should fix some edge cases. rustc does all of this very differently with plenty of queries for various forms of predicate lowering; but we have tight memory constraints so we prefer a different approach. --- crates/hir-def/src/hir/generics.rs | 2 +- crates/hir-def/src/lib.rs | 16 +- crates/hir-ty/src/generics.rs | 16 - crates/hir-ty/src/lib.rs | 63 ++- crates/hir-ty/src/lower.rs | 806 ++++++++++------------------- crates/hir-ty/src/lower/path.rs | 98 ++-- crates/hir-ty/src/utils.rs | 65 +-- crates/hir/src/lib.rs | 2 + crates/ide/src/signature_help.rs | 4 +- 9 files changed, 418 insertions(+), 654 deletions(-) diff --git a/crates/hir-def/src/hir/generics.rs b/crates/hir-def/src/hir/generics.rs index 482cf36f95..022f8adfdb 100644 --- a/crates/hir-def/src/hir/generics.rs +++ b/crates/hir-def/src/hir/generics.rs @@ -184,7 +184,7 @@ static EMPTY: LazyLock> = LazyLock::new(|| { impl GenericParams { /// The index of the self param in the generic of the non-parent definition. - pub(crate) const SELF_PARAM_ID_IN_SELF: la_arena::Idx = + pub const SELF_PARAM_ID_IN_SELF: la_arena::Idx = LocalTypeOrConstParamId::from_raw(RawIdx::from_u32(0)); pub fn new(db: &dyn DefDatabase, def: GenericDefId) -> Arc { diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 8d6c418d75..de674be05f 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -86,7 +86,7 @@ use crate::{ builtin_type::BuiltinType, db::DefDatabase, expr_store::ExpressionStoreSourceMap, - hir::generics::{LocalLifetimeParamId, LocalTypeOrConstParamId}, + hir::generics::{GenericParams, LocalLifetimeParamId, LocalTypeOrConstParamId}, nameres::{ LocalDefMap, assoc::{ImplItems, TraitItems}, @@ -553,15 +553,25 @@ pub struct TypeOrConstParamId { pub struct TypeParamId(TypeOrConstParamId); impl TypeParamId { + #[inline] pub fn parent(&self) -> GenericDefId { self.0.parent } + + #[inline] pub fn local_id(&self) -> LocalTypeOrConstParamId { self.0.local_id } -} -impl TypeParamId { + #[inline] + pub fn trait_self(trait_: TraitId) -> TypeParamId { + TypeParamId::from_unchecked(TypeOrConstParamId { + parent: trait_.into(), + local_id: GenericParams::SELF_PARAM_ID_IN_SELF, + }) + } + + #[inline] /// Caller should check if this toc id really belongs to a type pub fn from_unchecked(it: TypeOrConstParamId) -> Self { Self(it) diff --git a/crates/hir-ty/src/generics.rs b/crates/hir-ty/src/generics.rs index 5f0261437b..b1500bcdb7 100644 --- a/crates/hir-ty/src/generics.rs +++ b/crates/hir-ty/src/generics.rs @@ -224,22 +224,6 @@ impl Generics { } } -pub(crate) fn trait_self_param_idx(db: &dyn DefDatabase, def: GenericDefId) -> Option { - match def { - GenericDefId::TraitId(_) => { - let params = db.generic_params(def); - params.trait_self_param().map(|idx| idx.into_raw().into_u32() as usize) - } - GenericDefId::ImplId(_) => None, - _ => { - let parent_def = parent_generic_def(db, def)?; - let parent_params = db.generic_params(parent_def); - let parent_self_idx = parent_params.trait_self_param()?.into_raw().into_u32() as usize; - Some(parent_self_idx) - } - } -} - pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option { let container = match def { GenericDefId::FunctionId(it) => it.lookup(db).container, diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index f8920904f0..ca817ae713 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -57,9 +57,12 @@ mod test_db; #[cfg(test)] mod tests; -use std::hash::Hash; +use std::{hash::Hash, ops::ControlFlow}; -use hir_def::{CallableDefId, TypeOrConstParamId, type_ref::Rawness}; +use hir_def::{ + CallableDefId, GenericDefId, TypeAliasId, TypeOrConstParamId, TypeParamId, + hir::generics::GenericParams, resolver::TypeNs, type_ref::Rawness, +}; use hir_expand::name::Name; use indexmap::{IndexMap, map::Entry}; use intern::{Symbol, sym}; @@ -77,10 +80,11 @@ use crate::{ db::HirDatabase, display::{DisplayTarget, HirDisplay}, infer::unify::InferenceTable, + lower::SupertraitsInfo, next_solver::{ AliasTy, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, Canonical, - CanonicalVarKind, CanonicalVars, Const, ConstKind, DbInterner, FnSig, GenericArgs, - PolyFnSig, Predicate, Region, RegionKind, TraitRef, Ty, TyKind, Tys, abi, + CanonicalVarKind, CanonicalVars, ClauseKind, Const, ConstKind, DbInterner, FnSig, + GenericArgs, PolyFnSig, Predicate, Region, RegionKind, TraitRef, Ty, TyKind, Tys, abi, }, }; @@ -94,7 +98,7 @@ pub use infer::{ }; pub use lower::{ GenericPredicates, ImplTraits, LifetimeElisionKind, TyDefId, TyLoweringContext, ValueTyDefId, - associated_type_shorthand_candidates, diagnostics::*, + diagnostics::*, }; pub use next_solver::interner::{attach_db, attach_db_allow_change, with_attached_db}; pub use target_feature::TargetFeatures; @@ -478,6 +482,55 @@ where } } +/// To be used from `hir` only. +pub fn associated_type_shorthand_candidates( + db: &dyn HirDatabase, + def: GenericDefId, + res: TypeNs, + mut cb: impl FnMut(&Name, TypeAliasId) -> bool, +) -> Option { + let interner = DbInterner::new_no_crate(db); + let (def, param) = match res { + TypeNs::GenericParam(param) => (def, param), + TypeNs::SelfType(impl_) => { + let impl_trait = db.impl_trait(impl_)?.skip_binder().def_id.0; + let param = TypeParamId::from_unchecked(TypeOrConstParamId { + parent: impl_trait.into(), + local_id: GenericParams::SELF_PARAM_ID_IN_SELF, + }); + (impl_trait.into(), param) + } + _ => return None, + }; + + let mut dedup_map = FxHashSet::default(); + let param_ty = Ty::new_param(interner, param, param_idx(db, param.into()).unwrap() as u32); + // We use the ParamEnv and not the predicates because the ParamEnv elaborates bounds. + let param_env = db.trait_environment(def); + for clause in param_env.clauses { + let ClauseKind::Trait(trait_clause) = clause.kind().skip_binder() else { continue }; + if trait_clause.self_ty() != param_ty { + continue; + } + let trait_id = trait_clause.def_id().0; + dedup_map.extend( + SupertraitsInfo::query(db, trait_id) + .defined_assoc_types + .iter() + .map(|(name, id)| (name, *id)), + ); + } + + dedup_map + .into_iter() + .try_for_each( + |(name, id)| { + if cb(name, id) { ControlFlow::Break(id) } else { ControlFlow::Continue(()) } + }, + ) + .break_value() +} + /// To be used from `hir` only. pub fn callable_sig_from_fn_trait<'db>( self_ty: Ty<'db>, diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 386556b156..1290874177 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -20,7 +20,8 @@ use hir_def::{ builtin_type::BuiltinType, expr_store::{ExpressionStore, HygieneId, path::Path}, hir::generics::{ - GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate, + GenericParamDataRef, GenericParams, TypeOrConstParamData, TypeParamProvenance, + WherePredicate, }, item_tree::FieldsShape, lang_item::LangItems, @@ -36,27 +37,23 @@ use la_arena::{Arena, ArenaMap, Idx}; use path::{PathDiagnosticCallback, PathLoweringContext}; use rustc_ast_ir::Mutability; use rustc_hash::FxHashSet; -use rustc_pattern_analysis::Captures; use rustc_type_ir::{ AliasTyKind, BoundVarIndexKind, ConstKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, Interner, OutlivesPredicate, TermKind, TyKind::{self}, TypeFoldable, TypeVisitableExt, Upcast, UpcastFrom, elaborate, - inherent::{ - Clause as _, GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, - Ty as _, - }, + inherent::{Clause as _, GenericArgs as _, IntoKind as _, Region as _, Ty as _}, }; -use smallvec::{SmallVec, smallvec}; +use smallvec::SmallVec; use stdx::{impl_from, never}; use tracing::debug; use triomphe::{Arc, ThinArc}; use crate::{ - FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, all_super_traits, + FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, consteval::intern_const_ref, db::{HirDatabase, InternedOpaqueTyId}, - generics::{Generics, generics, trait_self_param_idx}, + generics::{Generics, generics}, next_solver::{ AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FxIndexMap, GenericArg, @@ -618,33 +615,12 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { &'b mut self, where_predicate: &'b WherePredicate, ignore_bindings: bool, - generics: &Generics, - predicate_filter: PredicateFilter, ) -> impl Iterator, GenericPredicateSource)> + use<'a, 'b, 'db> { match where_predicate { WherePredicate::ForLifetime { target, bound, .. } | WherePredicate::TypeBound { target, bound } => { - if let PredicateFilter::SelfTrait = predicate_filter { - let target_type = &self.store[*target]; - let self_type = 'is_self: { - if let TypeRef::Path(path) = target_type - && path.is_self_type() - { - break 'is_self true; - } - if let TypeRef::TypeParam(param) = target_type - && generics[param.local_id()].is_trait_self() - { - break 'is_self true; - } - false - }; - if !self_type { - return Either::Left(Either::Left(iter::empty())); - } - } let self_ty = self.lower_ty(*target); - Either::Left(Either::Right(self.lower_type_bound(bound, self_ty, ignore_bindings))) + Either::Left(self.lower_type_bound(bound, self_ty, ignore_bindings)) } &WherePredicate::Lifetime { bound, target } => Either::Right(iter::once(( Clause(Predicate::new( @@ -1626,6 +1602,92 @@ pub(crate) fn field_types_with_diagnostics_query<'db>( (res, create_diagnostics(ctx.diagnostics)) } +#[derive(Debug, PartialEq, Eq, Default)] +pub(crate) struct SupertraitsInfo { + /// This includes the trait itself. + pub(crate) all_supertraits: Box<[TraitId]>, + pub(crate) direct_supertraits: Box<[TraitId]>, + pub(crate) defined_assoc_types: Box<[(Name, TypeAliasId)]>, +} + +impl SupertraitsInfo { + #[inline] + pub(crate) fn query(db: &dyn HirDatabase, trait_: TraitId) -> &Self { + return supertraits_info(db, trait_); + + #[salsa::tracked(returns(ref), cycle_result = supertraits_info_cycle)] + fn supertraits_info(db: &dyn HirDatabase, trait_: TraitId) -> SupertraitsInfo { + let mut all_supertraits = FxHashSet::default(); + let mut direct_supertraits = FxHashSet::default(); + let mut defined_assoc_types = FxHashSet::default(); + + all_supertraits.insert(trait_); + defined_assoc_types.extend(trait_.trait_items(db).items.iter().filter_map( + |(name, id)| match *id { + AssocItemId::TypeAliasId(id) => Some((name.clone(), id)), + _ => None, + }, + )); + + let resolver = trait_.resolver(db); + let signature = db.trait_signature(trait_); + for pred in signature.generic_params.where_predicates() { + let (WherePredicate::TypeBound { target, bound } + | WherePredicate::ForLifetime { lifetimes: _, target, bound }) = pred + else { + continue; + }; + let (TypeBound::Path(bounded_trait, TraitBoundModifier::None) + | TypeBound::ForLifetime(_, bounded_trait)) = *bound + else { + continue; + }; + let target = &signature.store[*target]; + match target { + TypeRef::TypeParam(param) + if param.local_id() == GenericParams::SELF_PARAM_ID_IN_SELF => {} + TypeRef::Path(path) if path.is_self_type() => {} + _ => continue, + } + let Some(TypeNs::TraitId(bounded_trait)) = + resolver.resolve_path_in_type_ns_fully(db, &signature.store[bounded_trait]) + else { + continue; + }; + let SupertraitsInfo { + all_supertraits: bounded_trait_all_supertraits, + direct_supertraits: _, + defined_assoc_types: bounded_traits_defined_assoc_types, + } = SupertraitsInfo::query(db, bounded_trait); + all_supertraits.extend(bounded_trait_all_supertraits); + direct_supertraits.insert(bounded_trait); + defined_assoc_types.extend(bounded_traits_defined_assoc_types.iter().cloned()); + } + + SupertraitsInfo { + all_supertraits: Box::from_iter(all_supertraits), + direct_supertraits: Box::from_iter(direct_supertraits), + defined_assoc_types: Box::from_iter(defined_assoc_types), + } + } + + fn supertraits_info_cycle( + _db: &dyn HirDatabase, + _: salsa::Id, + _trait_: TraitId, + ) -> SupertraitsInfo { + SupertraitsInfo::default() + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum TypeParamAssocTypeShorthandError { + AssocTypeNotFound, + AmbiguousAssocType, + Cycle, +} + /// Predicates for `param_id` of the form `P: SomeTrait`. If /// `assoc_name` is provided, only return predicates referencing traits /// that have an associated type of that name. @@ -1640,15 +1702,14 @@ pub(crate) fn field_types_with_diagnostics_query<'db>( /// following bounds are disallowed: `T: Foo, U: Foo`, but /// these are fine: `T: Foo, U: Foo<()>`. #[tracing::instrument(skip(db), ret)] -#[salsa::tracked(returns(ref), cycle_result = generic_predicates_for_param_cycle_result)] -pub(crate) fn generic_predicates_for_param<'db>( - db: &'db dyn HirDatabase, +#[salsa::tracked(returns(ref), cycle_result = resolve_type_param_assoc_type_shorthand_cycle_result)] +fn resolve_type_param_assoc_type_shorthand( + db: &dyn HirDatabase, def: GenericDefId, - param_id: TypeOrConstParamId, - assoc_name: Option, -) -> StoredEarlyBinder { + param: TypeParamId, + assoc_name: Name, +) -> Result, TypeParamAssocTypeShorthandError> { let generics = generics(db, def); - let interner = DbInterner::new_no_crate(db); let resolver = def.resolver(db); let mut ctx = TyLoweringContext::new( db, @@ -1657,128 +1718,109 @@ pub(crate) fn generic_predicates_for_param<'db>( def, LifetimeElisionKind::AnonymousReportError, ); + let interner = ctx.interner; + let mut result = None; + let param_ty = Ty::new_param( + interner, + param, + generics.type_or_const_param_idx(param.into()).unwrap() as u32, + ); - // we have to filter out all other predicates *first*, before attempting to lower them - let has_relevant_bound = |pred: &_, ctx: &mut TyLoweringContext<'_, '_>| match pred { - WherePredicate::ForLifetime { target, bound, .. } - | WherePredicate::TypeBound { target, bound, .. } => { - let invalid_target = { ctx.lower_ty_only_param(*target) != Some(param_id) }; - if invalid_target { - // FIXME(sized-hierarchy): Revisit and adjust this properly once we have implemented - // sized-hierarchy correctly. - // If this is filtered out without lowering, `?Sized` or `PointeeSized` is not gathered into - // `ctx.unsized_types` - let lower = || -> bool { - match bound { - TypeBound::Path(_, TraitBoundModifier::Maybe) => true, - TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => { - let TypeRef::Path(path) = &ctx.store[path.type_ref()] else { - return false; - }; - let Some(pointee_sized) = ctx.lang_items.PointeeSized else { - return false; - }; - // Lower the path directly with `Resolver` instead of PathLoweringContext` - // to prevent diagnostics duplications. - ctx.resolver.resolve_path_in_type_ns_fully(ctx.db, path).is_some_and( - |it| matches!(it, TypeNs::TraitId(tr) if tr == pointee_sized), - ) - } - _ => false, - } - }(); - if lower { - ctx.lower_where_predicate(pred, true, &generics, PredicateFilter::All) - .for_each(drop); - } - return false; - } - - match bound { - &TypeBound::ForLifetime(_, path) | &TypeBound::Path(path, _) => { - // Only lower the bound if the trait could possibly define the associated - // type we're looking for. - let path = &ctx.store[path]; - - let Some(assoc_name) = &assoc_name else { return true }; - let Some(TypeNs::TraitId(tr)) = - resolver.resolve_path_in_type_ns_fully(db, path) - else { - return false; - }; - - trait_or_supertrait_has_assoc_type(db, tr, assoc_name) - } - TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false, - } + if let GenericDefId::TraitId(containing_trait) = param.parent() + && param.local_id() == GenericParams::SELF_PARAM_ID_IN_SELF + { + // Add the trait's own associated types. + if let Some(assoc_type) = + containing_trait.trait_items(db).associated_type_by_name(&assoc_name) + { + let args = GenericArgs::identity_for_item(interner, containing_trait.into()); + result = Some(StoredEarlyBinder::bind((assoc_type, args.store()))); } - WherePredicate::Lifetime { .. } => false, - }; - let mut predicates = Vec::new(); + } + for maybe_parent_generics in std::iter::successors(Some(&generics), |generics| generics.parent_generics()) { ctx.store = maybe_parent_generics.store(); for pred in maybe_parent_generics.where_predicates() { - if has_relevant_bound(pred, &mut ctx) { - predicates.extend( - ctx.lower_where_predicate( - pred, - true, - maybe_parent_generics, - PredicateFilter::All, - ) - .map(|(pred, _)| pred), - ); + let (WherePredicate::TypeBound { target, bound } + | WherePredicate::ForLifetime { lifetimes: _, target, bound }) = pred + else { + continue; + }; + let (TypeBound::Path(bounded_trait_path, TraitBoundModifier::None) + | TypeBound::ForLifetime(_, bounded_trait_path)) = *bound + else { + continue; + }; + let Some(target) = ctx.lower_ty_only_param(*target) else { continue }; + if target != param.into() { + continue; } + let Some(TypeNs::TraitId(bounded_trait)) = + resolver.resolve_path_in_type_ns_fully(db, &ctx.store[bounded_trait_path]) + else { + continue; + }; + if !SupertraitsInfo::query(db, bounded_trait) + .defined_assoc_types + .iter() + .any(|(name, _)| *name == assoc_name) + { + continue; + } + + let Some((bounded_trait_ref, _)) = + ctx.lower_trait_ref_from_path(bounded_trait_path, param_ty) + else { + continue; + }; + // Now, search from the start on the *bounded* trait like if we wrote `Self::Assoc`. Eventually, we'll get + // the correct trait ref (or a cycle). + let lookup_on_bounded_trait = resolve_type_param_assoc_type_shorthand( + db, + bounded_trait.into(), + TypeParamId::trait_self(bounded_trait), + assoc_name.clone(), + ); + let lookup_on_bounded_trait = match lookup_on_bounded_trait { + Ok(it) => it, + Err( + err @ (TypeParamAssocTypeShorthandError::AmbiguousAssocType + | TypeParamAssocTypeShorthandError::Cycle), + ) => return Err(*err), + Err(TypeParamAssocTypeShorthandError::AssocTypeNotFound) => { + never!("we checked that the trait defines this assoc type"); + continue; + } + }; + let (assoc_type, args) = lookup_on_bounded_trait + .get_with(|(assoc_type, args)| (*assoc_type, args.as_ref())) + .skip_binder(); + let args = EarlyBinder::bind(args).instantiate(interner, bounded_trait_ref.args); + let current_result = StoredEarlyBinder::bind((assoc_type, args.store())); + // If we already have a result, this is an ambiguity - unless this is the same result, then we are fine + // (e.g. rustc allows to write the same bound twice without ambiguity). + if let Some(existing_result) = result + && existing_result != current_result + { + return Err(TypeParamAssocTypeShorthandError::AmbiguousAssocType); + } + result = Some(current_result); } } - let args = GenericArgs::identity_for_item(interner, def.into()); - if !args.is_empty() { - let explicitly_unsized_tys = ctx.unsized_types; - if let Some(implicitly_sized_predicates) = implicitly_sized_clauses( - db, - ctx.lang_items, - param_id.parent, - &explicitly_unsized_tys, - &args, - ) { - predicates.extend(implicitly_sized_predicates); - }; - } - StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()) + result.ok_or(TypeParamAssocTypeShorthandError::AssocTypeNotFound) } -pub(crate) fn generic_predicates_for_param_cycle_result( - db: &dyn HirDatabase, +fn resolve_type_param_assoc_type_shorthand_cycle_result( + _db: &dyn HirDatabase, _: salsa::Id, _def: GenericDefId, - _param_id: TypeOrConstParamId, - _assoc_name: Option, -) -> StoredEarlyBinder { - StoredEarlyBinder::bind(Clauses::empty(DbInterner::new_no_crate(db)).store()) -} - -/// Check if this trait or any of its supertraits define an associated -/// type with the given name. -fn trait_or_supertrait_has_assoc_type( - db: &dyn HirDatabase, - tr: TraitId, - assoc_name: &Name, -) -> bool { - for trait_id in all_super_traits(db, tr) { - if trait_id - .trait_items(db) - .items - .iter() - .any(|(name, item)| matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name) - { - return true; - } - } - - false + _param: TypeParamId, + _assoc_name: Name, +) -> Result, TypeParamAssocTypeShorthandError> { + Err(TypeParamAssocTypeShorthandError::Cycle) } #[inline] @@ -1904,7 +1946,7 @@ impl<'db> GenericPredicates { db: &'db dyn HirDatabase, def: GenericDefId, ) -> (GenericPredicates, Diagnostics) { - generic_predicates_filtered_by(db, def, PredicateFilter::All, |_| true) + generic_predicates(db, def) } } @@ -2042,24 +2084,10 @@ pub(crate) fn trait_environment<'db>(db: &'db dyn HirDatabase, def: GenericDefId } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(crate) enum PredicateFilter { - SelfTrait, - All, -} - /// Resolve the where clause(s) of an item with generics, /// with a given filter -#[tracing::instrument(skip(db, filter), ret)] -pub(crate) fn generic_predicates_filtered_by( - db: &dyn HirDatabase, - def: GenericDefId, - predicate_filter: PredicateFilter, - filter: F, -) -> (GenericPredicates, Diagnostics) -where - F: Fn(GenericDefId) -> bool, -{ +#[tracing::instrument(skip(db), ret)] +fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredicates, Diagnostics) { let generics = generics(db, def); let resolver = def.resolver(db); let interner = DbInterner::new_no_crate(db); @@ -2081,9 +2109,9 @@ where let all_generics = std::iter::successors(Some(&generics), |generics| generics.parent_generics()) .collect::>(); - let own_implicit_trait_predicate = implicit_trait_predicate(interner, def, predicate_filter); + let own_implicit_trait_predicate = implicit_trait_predicate(interner, def); let parent_implicit_trait_predicate = if all_generics.len() > 1 { - implicit_trait_predicate(interner, all_generics.last().unwrap().def(), predicate_filter) + implicit_trait_predicate(interner, all_generics.last().unwrap().def()) } else { None }; @@ -2091,97 +2119,85 @@ where // Collect only diagnostics from the child, not including parents. ctx.diagnostics.clear(); - if filter(maybe_parent_generics.def()) { - ctx.store = maybe_parent_generics.store(); - for pred in maybe_parent_generics.where_predicates() { - tracing::debug!(?pred); - for (pred, source) in - ctx.lower_where_predicate(pred, false, maybe_parent_generics, predicate_filter) - { - match source { - GenericPredicateSource::SelfOnly => { - if maybe_parent_generics.def() == def { - own_predicates.push(pred); - } else { - parent_predicates.push(pred); - } + ctx.store = maybe_parent_generics.store(); + for pred in maybe_parent_generics.where_predicates() { + tracing::debug!(?pred); + for (pred, source) in ctx.lower_where_predicate(pred, false) { + match source { + GenericPredicateSource::SelfOnly => { + if maybe_parent_generics.def() == def { + own_predicates.push(pred); + } else { + parent_predicates.push(pred); } - GenericPredicateSource::AssocTyBound => { - if maybe_parent_generics.def() == def { - own_assoc_ty_bounds.push(pred); - } else { - parent_assoc_ty_bounds.push(pred); - } + } + GenericPredicateSource::AssocTyBound => { + if maybe_parent_generics.def() == def { + own_assoc_ty_bounds.push(pred); + } else { + parent_assoc_ty_bounds.push(pred); } } } } - - if maybe_parent_generics.def() == def { - push_const_arg_has_type_predicates(db, &mut own_predicates, maybe_parent_generics); - } else { - push_const_arg_has_type_predicates( - db, - &mut parent_predicates, - maybe_parent_generics, - ); - } - - if let Some(sized_trait) = sized_trait { - let mut add_sized_clause = |param_idx, param_id, param_data| { - let ( - GenericParamId::TypeParamId(param_id), - GenericParamDataRef::TypeParamData(param_data), - ) = (param_id, param_data) - else { - return; - }; - - if param_data.provenance == TypeParamProvenance::TraitSelf { - return; - } - - let param_ty = Ty::new_param(interner, param_id, param_idx); - if ctx.unsized_types.contains(¶m_ty) { - return; - } - let trait_ref = TraitRef::new_from_args( - interner, - sized_trait.into(), - GenericArgs::new_from_slice(&[param_ty.into()]), - ); - let clause = Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), - )); - if maybe_parent_generics.def() == def { - own_predicates.push(clause); - } else { - parent_predicates.push(clause); - } - }; - let parent_params_len = maybe_parent_generics.len_parent(); - maybe_parent_generics.iter_self().enumerate().for_each( - |(param_idx, (param_id, param_data))| { - add_sized_clause( - (param_idx + parent_params_len) as u32, - param_id, - param_data, - ); - }, - ); - } - - // We do not clear `ctx.unsized_types`, as the `?Sized` clause of a child (e.g. an associated type) can - // be declared on the parent (e.g. the trait). It is nevertheless fine to register the implicit `Sized` - // predicates before lowering the child, as a child cannot define a `?Sized` predicate for its parent. - // But we do have to lower the parent first. } + + if maybe_parent_generics.def() == def { + push_const_arg_has_type_predicates(db, &mut own_predicates, maybe_parent_generics); + } else { + push_const_arg_has_type_predicates(db, &mut parent_predicates, maybe_parent_generics); + } + + if let Some(sized_trait) = sized_trait { + let mut add_sized_clause = |param_idx, param_id, param_data| { + let ( + GenericParamId::TypeParamId(param_id), + GenericParamDataRef::TypeParamData(param_data), + ) = (param_id, param_data) + else { + return; + }; + + if param_data.provenance == TypeParamProvenance::TraitSelf { + return; + } + + let param_ty = Ty::new_param(interner, param_id, param_idx); + if ctx.unsized_types.contains(¶m_ty) { + return; + } + let trait_ref = TraitRef::new_from_args( + interner, + sized_trait.into(), + GenericArgs::new_from_slice(&[param_ty.into()]), + ); + let clause = Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )); + if maybe_parent_generics.def() == def { + own_predicates.push(clause); + } else { + parent_predicates.push(clause); + } + }; + let parent_params_len = maybe_parent_generics.len_parent(); + maybe_parent_generics.iter_self().enumerate().for_each( + |(param_idx, (param_id, param_data))| { + add_sized_clause((param_idx + parent_params_len) as u32, param_id, param_data); + }, + ); + } + + // We do not clear `ctx.unsized_types`, as the `?Sized` clause of a child (e.g. an associated type) can + // be declared on the parent (e.g. the trait). It is nevertheless fine to register the implicit `Sized` + // predicates before lowering the child, as a child cannot define a `?Sized` predicate for its parent. + // But we do have to lower the parent first. } let diagnostics = create_diagnostics(ctx.diagnostics); @@ -2229,7 +2245,6 @@ where fn implicit_trait_predicate<'db>( interner: DbInterner<'db>, def: GenericDefId, - predicate_filter: PredicateFilter, ) -> Option> { // For traits, add `Self: Trait` predicate. This is // not part of the predicates that a user writes, but it @@ -2243,9 +2258,7 @@ where // prove that the trait applies to the types that were // used, and adding the predicate into this list ensures // that this is done. - if let GenericDefId::TraitId(def_id) = def - && predicate_filter == PredicateFilter::All - { + if let GenericDefId::TraitId(def_id) = def { Some(TraitRef::identity(interner, def_id.into()).upcast(interner)) } else { None @@ -2282,49 +2295,6 @@ fn push_const_arg_has_type_predicates<'db>( } } -/// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound. -/// Exception is Self of a trait def. -fn implicitly_sized_clauses<'a, 'subst, 'db>( - db: &'db dyn HirDatabase, - lang_items: &LangItems, - def: GenericDefId, - explicitly_unsized_tys: &'a FxHashSet>, - args: &'subst GenericArgs<'db>, -) -> Option> + Captures<'a> + Captures<'subst>> { - let interner = DbInterner::new_no_crate(db); - let sized_trait = lang_items.Sized?; - - let trait_self_idx = trait_self_param_idx(db, def); - - Some( - args.iter() - .enumerate() - .filter_map( - move |(idx, generic_arg)| { - if Some(idx) == trait_self_idx { None } else { Some(generic_arg) } - }, - ) - .filter_map(|generic_arg| generic_arg.as_type()) - .filter(move |self_ty| !explicitly_unsized_tys.contains(self_ty)) - .map(move |self_ty| { - let trait_ref = TraitRef::new_from_args( - interner, - sized_trait.into(), - GenericArgs::new_from_slice(&[self_ty.into()]), - ); - Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), - )) - }), - ) -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GenericDefaults(Option>]>>); @@ -2602,235 +2572,19 @@ pub(crate) fn associated_ty_item_bounds<'db>( pub(crate) fn associated_type_by_name_including_super_traits<'db>( db: &'db dyn HirDatabase, trait_ref: TraitRef<'db>, - name: &Name, -) -> Option<(TraitRef<'db>, TypeAliasId)> { - let module = trait_ref.def_id.0.module(db); - let interner = DbInterner::new_with(db, module.krate(db)); - all_supertraits_trait_refs(db, trait_ref.def_id.0) - .map(|t| t.instantiate(interner, trait_ref.args)) - .find_map(|t| { - let trait_id = t.def_id.0; - let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?; - Some((t, assoc_type)) - }) -} - -pub fn associated_type_shorthand_candidates( - db: &dyn HirDatabase, - def: GenericDefId, - res: TypeNs, - mut cb: impl FnMut(&Name, TypeAliasId) -> bool, -) -> Option { + name: Name, +) -> Option<(TypeAliasId, GenericArgs<'db>)> { + let assoc_type = resolve_type_param_assoc_type_shorthand( + db, + trait_ref.def_id.0.into(), + TypeParamId::trait_self(trait_ref.def_id.0), + name.clone(), + ) + .as_ref() + .ok()?; + let (assoc_type, trait_args) = assoc_type + .get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref())) + .skip_binder(); let interner = DbInterner::new_no_crate(db); - 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, - mut check_alias: impl FnMut(&Name, TraitRef<'db>, TypeAliasId) -> Option, -) -> Option { - let db = interner.db; - let mut search = |t: TraitRef<'db>| -> Option { - let mut checked_traits = FxHashSet::default(); - let mut check_trait = |trait_ref: TraitRef<'db>| { - let trait_id = trait_ref.def_id.0; - 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, trait_ref, alias) - { - return Some(ty); - } - } - None - }; - let mut stack: SmallVec<[_; 4]> = smallvec![t]; - while let Some(trait_ref) = stack.pop() { - if let Some(alias) = check_trait(trait_ref) { - return Some(alias); - } - let predicates = generic_predicates_filtered_by( - db, - GenericDefId::TraitId(trait_ref.def_id.0), - PredicateFilter::SelfTrait, - // We are likely in the midst of lowering generic predicates of `def`. - // So, if we allow `pred == def` we might fall into an infinite recursion. - // Actually, we have already checked for the case `pred == def` above as we started - // with a stack including `trait_id` - |pred| pred != def && pred == GenericDefId::TraitId(trait_ref.def_id.0), - ) - .0 - .predicates; - for pred in predicates.get().instantiate_identity() { - tracing::debug!(?pred); - let sup_trait_ref = match pred.kind().skip_binder() { - rustc_type_ir::ClauseKind::Trait(pred) => pred.trait_ref, - _ => continue, - }; - let sup_trait_ref = - EarlyBinder::bind(sup_trait_ref).instantiate(interner, trait_ref.args); - stack.push(sup_trait_ref); - } - tracing::debug!(?stack); - } - - None - }; - - match res { - TypeNs::SelfType(impl_id) => { - let trait_ref = db.impl_trait(impl_id)?; - - // FIXME(next-solver): same method in `lower` checks for impl or not - // Is that needed here? - - // 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, - 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 = 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 = - generic_predicates_for_param(db, def, param_id.into(), assoc_name.clone()); - predicates - .get() - .iter_identity() - .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, - } -} - -/// During lowering, elaborating supertraits can cause cycles. To avoid that, we have a separate query -/// to only collect supertraits. -/// -/// Technically, it is possible to avoid even more cycles by only collecting the `TraitId` of supertraits -/// without their args. However rustc doesn't do that, so we don't either. -pub(crate) fn all_supertraits_trait_refs( - db: &dyn HirDatabase, - trait_: TraitId, -) -> impl ExactSizeIterator>> { - let interner = DbInterner::new_no_crate(db); - return all_supertraits_trait_refs_query(db, trait_).iter().map(move |trait_ref| { - trait_ref.get_with(|(trait_, args)| { - TraitRef::new_from_args(interner, (*trait_).into(), args.as_ref()) - }) - }); - - #[salsa_macros::tracked(returns(deref), cycle_result = all_supertraits_trait_refs_cycle_result)] - pub(crate) fn all_supertraits_trait_refs_query( - db: &dyn HirDatabase, - trait_: TraitId, - ) -> Box<[StoredEarlyBinder<(TraitId, StoredGenericArgs)>]> { - let resolver = trait_.resolver(db); - let signature = db.trait_signature(trait_); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &signature.store, - trait_.into(), - LifetimeElisionKind::AnonymousReportError, - ); - let interner = ctx.interner; - - let self_param_ty = Ty::new_param( - interner, - TypeParamId::from_unchecked(TypeOrConstParamId { - parent: trait_.into(), - local_id: Idx::from_raw(la_arena::RawIdx::from_u32(0)), - }), - 0, - ); - - let mut supertraits = FxHashSet::default(); - supertraits.insert(StoredEarlyBinder::bind(( - trait_, - GenericArgs::identity_for_item(interner, trait_.into()).store(), - ))); - - for pred in signature.generic_params.where_predicates() { - let WherePredicate::TypeBound { target, bound } = pred else { - continue; - }; - let target = &signature.store[*target]; - if let TypeRef::TypeParam(param_id) = target - && param_id.local_id().into_raw().into_u32() == 0 - { - // This is `Self`. - } else if let TypeRef::Path(path) = target - && path.is_self_type() - { - // Also `Self`. - } else { - // Not `Self`! - continue; - } - - ctx.lower_type_bound(bound, self_param_ty, true).for_each(|(clause, _)| { - if let ClauseKind::Trait(trait_ref) = clause.kind().skip_binder() { - supertraits.extend( - all_supertraits_trait_refs(db, trait_ref.trait_ref.def_id.0).map(|t| { - let trait_ref = t.instantiate(interner, trait_ref.trait_ref.args); - StoredEarlyBinder::bind((trait_ref.def_id.0, trait_ref.args.store())) - }), - ); - } - }); - } - - Box::from_iter(supertraits) - } - - pub(crate) fn all_supertraits_trait_refs_cycle_result( - db: &dyn HirDatabase, - _: salsa::Id, - trait_: TraitId, - ) -> Box<[StoredEarlyBinder<(TraitId, StoredGenericArgs)>]> { - let interner = DbInterner::new_no_crate(db); - Box::new([StoredEarlyBinder::bind(( - trait_, - GenericArgs::identity_for_item(interner, trait_.into()).store(), - ))]) - } + Some((assoc_type, EarlyBinder::bind(trait_args).instantiate(interner, trait_ref.args))) } diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs index f3d0de1227..ab686a9aac 100644 --- a/crates/hir-ty/src/lower/path.rs +++ b/crates/hir-ty/src/lower/path.rs @@ -2,7 +2,7 @@ use either::Either; use hir_def::{ - GenericDefId, GenericParamId, Lookup, TraitId, TypeAliasId, + GenericDefId, GenericParamId, Lookup, TraitId, TypeParamId, expr_store::{ ExpressionStore, HygieneId, path::{ @@ -17,7 +17,6 @@ use hir_def::{ signatures::TraitFlags, type_ref::{TypeRef, TypeRefId}, }; -use hir_expand::name::Name; use rustc_type_ir::{ AliasTerm, AliasTy, AliasTyKind, inherent::{GenericArgs as _, Region as _, Ty as _}, @@ -31,13 +30,10 @@ use crate::{ consteval::{unknown_const, unknown_const_as_generic}, db::HirDatabase, generics::{Generics, generics}, - lower::{ - GenericPredicateSource, LifetimeElisionKind, PathDiagnosticCallbackData, - named_associated_type_shorthand_candidates, - }, + lower::{GenericPredicateSource, LifetimeElisionKind, PathDiagnosticCallbackData}, next_solver::{ - Binder, Clause, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Predicate, - ProjectionPredicate, Region, TraitRef, Ty, + Binder, Clause, Const, DbInterner, EarlyBinder, ErrorGuaranteed, GenericArg, GenericArgs, + Predicate, ProjectionPredicate, Region, TraitRef, Ty, }, }; @@ -481,43 +477,59 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { #[tracing::instrument(skip(self), ret)] fn select_associated_type(&mut self, res: Option, infer_args: bool) -> Ty<'db> { let interner = self.ctx.interner; - let Some(res) = res else { - return Ty::new_error(self.ctx.interner, ErrorGuaranteed); - }; + let db = self.ctx.db; let def = self.ctx.def; let segment = self.current_or_prev_segment; let assoc_name = segment.name; - let check_alias = |name: &Name, t: TraitRef<'db>, associated_ty: TypeAliasId| { - if name != assoc_name { - return None; + let error_ty = || Ty::new_error(self.ctx.interner, ErrorGuaranteed); + let (assoc_type, trait_args) = match res { + Some(TypeNs::GenericParam(param)) => { + let Ok(assoc_type) = super::resolve_type_param_assoc_type_shorthand( + db, + def, + param, + assoc_name.clone(), + ) else { + return error_ty(); + }; + assoc_type + .get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref())) + .skip_binder() } - - // 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(), infer_args, None, true); - - let substs = GenericArgs::new_from_iter( - interner, - t.args.iter().chain(substs.iter().skip(t.args.len())), - ); - - Some(Ty::new_alias( - interner, - AliasTyKind::Projection, - AliasTy::new_from_args(interner, associated_ty.into(), substs), - )) + Some(TypeNs::SelfType(impl_)) => { + let Some(impl_trait) = db.impl_trait(impl_) else { + return error_ty(); + }; + let impl_trait = impl_trait.instantiate_identity(); + // Searching for `Self::Assoc` in `impl Trait for Type` is like searching for `Self::Assoc` in `Trait`. + let Ok(assoc_type) = super::resolve_type_param_assoc_type_shorthand( + db, + impl_trait.def_id.0.into(), + TypeParamId::trait_self(impl_trait.def_id.0), + assoc_name.clone(), + ) else { + return error_ty(); + }; + let (assoc_type, trait_args) = assoc_type + .get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref())) + .skip_binder(); + (assoc_type, EarlyBinder::bind(trait_args).instantiate(interner, impl_trait.args)) + } + _ => return error_ty(), }; - named_associated_type_shorthand_candidates( + + // 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(assoc_type.into(), infer_args, None, true); + + let substs = GenericArgs::new_from_iter( interner, - def, - res, - Some(assoc_name.clone()), - check_alias, - ) - .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) + trait_args.iter().chain(substs.iter().skip(trait_args.len())), + ); + + Ty::new_projection_from_args(interner, assoc_type.into(), substs) } fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> { @@ -862,9 +874,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { let found = associated_type_by_name_including_super_traits( self.ctx.db, trait_ref, - &binding.name, + binding.name.clone(), ); - let (super_trait_ref, associated_ty) = match found { + let (associated_ty, super_trait_args) = match found { None => return SmallVec::new(), Some(t) => t, }; @@ -878,7 +890,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { binding.args.as_ref(), associated_ty.into(), false, // this is not relevant - Some(super_trait_ref.self_ty()), + Some(super_trait_args.type_at(0)), PathGenericsSource::AssocType { segment: this.current_segment_u32(), assoc_type: binding_idx as u32, @@ -889,7 +901,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }); let args = GenericArgs::new_from_iter( interner, - super_trait_ref.args.iter().chain(args.iter().skip(super_trait_ref.args.len())), + super_trait_args.iter().chain(args.iter().skip(super_trait_args.len())), ); let projection_term = AliasTerm::new_from_args(interner, associated_ty.into(), args); diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs index 148300deb8..be64f55ea5 100644 --- a/crates/hir-ty/src/utils.rs +++ b/crates/hir-ty/src/utils.rs @@ -1,28 +1,19 @@ //! 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::cell::LazyCell; - use base_db::target::{self, TargetData}; use hir_def::{ - EnumId, EnumVariantId, FunctionId, Lookup, TraitId, - attrs::AttrFlags, - db::DefDatabase, - hir::generics::WherePredicate, - lang_item::LangItems, - resolver::{HasResolver, TypeNs}, - type_ref::{TraitBoundModifier, TypeRef}, + EnumId, EnumVariantId, FunctionId, Lookup, TraitId, attrs::AttrFlags, lang_item::LangItems, }; use intern::sym; use rustc_abi::TargetDataLayout; -use smallvec::{SmallVec, smallvec}; use span::Edition; use crate::{ TargetFeatures, db::HirDatabase, layout::{Layout, TagEncoding}, - lower::all_supertraits_trait_refs, + lower::SupertraitsInfo, mir::pad16, }; @@ -51,55 +42,13 @@ pub(crate) fn fn_traits(lang_items: &LangItems) -> impl Iterator } /// Returns an iterator over the direct super traits (including the trait itself). -pub fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> { - let mut result = smallvec![trait_]; - direct_super_traits_cb(db, trait_, |tt| { - if !result.contains(&tt) { - result.push(tt); - } - }); - result +pub fn direct_super_traits(db: &dyn HirDatabase, trait_: TraitId) -> &[TraitId] { + &SupertraitsInfo::query(db, trait_).direct_supertraits } -/// Returns an iterator over the whole super trait hierarchy (including the -/// trait itself). -pub fn all_super_traits(db: &dyn HirDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> { - let mut supertraits = all_supertraits_trait_refs(db, trait_) - .map(|trait_ref| trait_ref.skip_binder().def_id.0) - .collect::>(); - supertraits.sort_unstable(); - supertraits.dedup(); - supertraits -} - -fn direct_super_traits_cb(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(TraitId)) { - let resolver = LazyCell::new(|| trait_.resolver(db)); - let (generic_params, store) = db.generic_params_and_store(trait_.into()); - let trait_self = generic_params.trait_self_param(); - generic_params - .where_predicates() - .iter() - .filter_map(|pred| match pred { - WherePredicate::ForLifetime { target, bound, .. } - | WherePredicate::TypeBound { target, bound } => { - let is_trait = match &store[*target] { - TypeRef::Path(p) => p.is_self_type(), - TypeRef::TypeParam(p) => Some(p.local_id()) == trait_self, - _ => false, - }; - match is_trait { - true => bound.as_path(&store), - false => None, - } - } - WherePredicate::Lifetime { .. } => None, - }) - .filter(|(_, bound_modifier)| matches!(bound_modifier, TraitBoundModifier::None)) - .filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path) { - Some(TypeNs::TraitId(t)) => Some(t), - _ => None, - }) - .for_each(cb); +/// Returns the whole super trait hierarchy (including the trait itself). +pub fn all_super_traits(db: &dyn HirDatabase, trait_: TraitId) -> &[TraitId] { + &SupertraitsInfo::query(db, trait_).all_supertraits } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 5820a6714b..9dbee16dae 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -6167,6 +6167,7 @@ impl<'db> Type<'db> { self.autoderef_(db) .filter_map(|ty| ty.dyn_trait()) .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db, dyn_trait_id)) + .copied() .map(Trait::from) } @@ -6184,6 +6185,7 @@ impl<'db> Type<'db> { _ => None, }) .flat_map(|t| hir_ty::all_super_traits(db, t)) + .copied() }) .map(Trait::from) } diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index f86974b4ec..9ab07565e9 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -1975,8 +1975,8 @@ trait Sub: Super + Super { fn f() -> impl Sub<$0 "#, expect![[r#" - trait Sub - ^^^^^^^^^^^ --------- + trait Sub + ^^^^^^^^^ ----------- "#]], ); }