Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #21745 from ChayimFriedman2/assoc-type-fixup
fix: Fix a bug in associated type lowering
Lukas Wirth 7 weeks ago
parent 665fec5 · parent bfa88ee · commit e8b19c9
-rw-r--r--crates/hir-ty/src/lower.rs105
-rw-r--r--crates/hir-ty/src/lower/path.rs39
-rw-r--r--crates/hir-ty/src/tests/regression.rs20
3 files changed, 110 insertions, 54 deletions
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 99b736bf4d..c49e943437 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -1680,10 +1680,16 @@ impl SupertraitsInfo {
}
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-enum TypeParamAssocTypeShorthandError {
- AssocTypeNotFound,
- AmbiguousAssocType,
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+enum AssocTypeShorthandResolution {
+ Resolved(StoredEarlyBinder<(TypeAliasId, StoredGenericArgs)>),
+ Ambiguous {
+ /// If one resolution belongs to a sub-trait and one to a supertrait, this contains
+ /// the sub-trait's resolution. This can be `None` if there is no trait inheritance
+ /// relationship between the resolutions.
+ sub_trait_resolution: Option<StoredEarlyBinder<(TypeAliasId, StoredGenericArgs)>>,
+ },
+ NotFound,
Cycle,
}
@@ -1707,7 +1713,7 @@ fn resolve_type_param_assoc_type_shorthand(
def: GenericDefId,
param: TypeParamId,
assoc_name: Name,
-) -> Result<StoredEarlyBinder<(TypeAliasId, StoredGenericArgs)>, TypeParamAssocTypeShorthandError> {
+) -> AssocTypeShorthandResolution {
let generics = generics(db, def);
let resolver = def.resolver(db);
let mut ctx = TyLoweringContext::new(
@@ -1718,13 +1724,13 @@ fn resolve_type_param_assoc_type_shorthand(
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,
);
+ let mut this_trait_resolution = None;
if let GenericDefId::TraitId(containing_trait) = param.parent()
&& param.local_id() == GenericParams::SELF_PARAM_ID_IN_SELF
{
@@ -1733,10 +1739,11 @@ fn resolve_type_param_assoc_type_shorthand(
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())));
+ this_trait_resolution = Some(StoredEarlyBinder::bind((assoc_type, args.store())));
}
}
+ let mut supertraits_resolution = None;
for maybe_parent_generics in
std::iter::successors(Some(&generics), |generics| generics.parent_generics())
{
@@ -1782,34 +1789,53 @@ fn resolve_type_param_assoc_type_shorthand(
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) => {
+ let assoc_type_and_args = match &lookup_on_bounded_trait {
+ AssocTypeShorthandResolution::Resolved(trait_ref) => trait_ref,
+ AssocTypeShorthandResolution::Ambiguous {
+ sub_trait_resolution: Some(trait_ref),
+ } => trait_ref,
+ AssocTypeShorthandResolution::Ambiguous { sub_trait_resolution: None } => {
+ return AssocTypeShorthandResolution::Ambiguous {
+ sub_trait_resolution: this_trait_resolution,
+ };
+ }
+ AssocTypeShorthandResolution::NotFound => {
never!("we checked that the trait defines this assoc type");
continue;
}
+ AssocTypeShorthandResolution::Cycle => return AssocTypeShorthandResolution::Cycle,
};
- 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);
+ if let Some(this_trait_resolution) = this_trait_resolution {
+ return AssocTypeShorthandResolution::Ambiguous {
+ sub_trait_resolution: Some(this_trait_resolution),
+ };
+ } else if supertraits_resolution.is_some() {
+ return AssocTypeShorthandResolution::Ambiguous { sub_trait_resolution: None };
+ } else {
+ let (assoc_type, args) = assoc_type_and_args
+ .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()));
+ supertraits_resolution = Some(match lookup_on_bounded_trait {
+ AssocTypeShorthandResolution::Resolved(_) => {
+ AssocTypeShorthandResolution::Resolved(current_result)
+ }
+ AssocTypeShorthandResolution::Ambiguous { .. } => {
+ AssocTypeShorthandResolution::Ambiguous {
+ sub_trait_resolution: Some(current_result),
+ }
+ }
+ AssocTypeShorthandResolution::NotFound
+ | AssocTypeShorthandResolution::Cycle => unreachable!(),
+ });
}
- result = Some(current_result);
}
}
- result.ok_or(TypeParamAssocTypeShorthandError::AssocTypeNotFound)
+ supertraits_resolution
+ .or_else(|| this_trait_resolution.map(AssocTypeShorthandResolution::Resolved))
+ .unwrap_or(AssocTypeShorthandResolution::NotFound)
}
fn resolve_type_param_assoc_type_shorthand_cycle_result(
@@ -1818,8 +1844,8 @@ fn resolve_type_param_assoc_type_shorthand_cycle_result(
_def: GenericDefId,
_param: TypeParamId,
_assoc_name: Name,
-) -> Result<StoredEarlyBinder<(TypeAliasId, StoredGenericArgs)>, TypeParamAssocTypeShorthandError> {
- Err(TypeParamAssocTypeShorthandError::Cycle)
+) -> AssocTypeShorthandResolution {
+ AssocTypeShorthandResolution::Cycle
}
#[inline]
@@ -2568,19 +2594,22 @@ pub(crate) fn associated_ty_item_bounds<'db>(
EarlyBinder::bind(BoundExistentialPredicates::new_from_slice(&bounds))
}
-pub(crate) fn associated_type_by_name_including_super_traits<'db>(
+pub(crate) fn associated_type_by_name_including_super_traits_allow_ambiguity<'db>(
db: &'db dyn HirDatabase,
trait_ref: TraitRef<'db>,
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 (AssocTypeShorthandResolution::Resolved(assoc_type)
+ | AssocTypeShorthandResolution::Ambiguous { sub_trait_resolution: Some(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(),
+ )
+ else {
+ return None;
+ };
let (assoc_type, trait_args) = assoc_type
.get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref()))
.skip_binder();
diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs
index d47d696259..79f29d370f 100644
--- a/crates/hir-ty/src/lower/path.rs
+++ b/crates/hir-ty/src/lower/path.rs
@@ -30,7 +30,10 @@ use crate::{
consteval::{unknown_const, unknown_const_as_generic},
db::HirDatabase,
generics::{Generics, generics},
- lower::{GenericPredicateSource, LifetimeElisionKind, PathDiagnosticCallbackData},
+ lower::{
+ AssocTypeShorthandResolution, GenericPredicateSource, LifetimeElisionKind,
+ PathDiagnosticCallbackData,
+ },
next_solver::{
Binder, Clause, Const, DbInterner, EarlyBinder, ErrorGuaranteed, GenericArg, GenericArgs,
Predicate, ProjectionPredicate, Region, TraitRef, Ty,
@@ -38,8 +41,8 @@ use crate::{
};
use super::{
- ImplTraitLoweringMode, TyLoweringContext, associated_type_by_name_including_super_traits,
- const_param_ty_query, ty_query,
+ ImplTraitLoweringMode, TyLoweringContext,
+ associated_type_by_name_including_super_traits_allow_ambiguity, const_param_ty_query, ty_query,
};
type CallbackData<'a> =
@@ -482,12 +485,14 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
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 {
+ let AssocTypeShorthandResolution::Resolved(assoc_type) =
+ super::resolve_type_param_assoc_type_shorthand(
+ db,
+ def,
+ param,
+ assoc_name.clone(),
+ )
+ else {
return error_ty();
};
assoc_type
@@ -500,12 +505,14 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
};
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 {
+ let AssocTypeShorthandResolution::Resolved(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
@@ -869,7 +876,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
let interner = self.ctx.interner;
self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| {
args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| {
- let found = associated_type_by_name_including_super_traits(
+ let found = associated_type_by_name_including_super_traits_allow_ambiguity(
self.ctx.db,
trait_ref,
binding.name.clone(),
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 658c304aac..1939db0ef5 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -2795,3 +2795,23 @@ fn foo() {
"#]],
);
}
+
+#[test]
+fn regression_21742() {
+ check_no_mismatches(
+ r#"
+pub trait IntoIterator {
+ type Item;
+}
+
+pub trait Collection: IntoIterator<Item = <Self as Collection>::Item> {
+ type Item;
+ fn contains(&self, item: &<Self as Collection>::Item);
+}
+
+fn contains_0<S: Collection<Item = i32>>(points: &S) {
+ points.contains(&0)
+}
+ "#,
+ );
+}