Skip to content
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
38 changes: 30 additions & 8 deletions crates/hir-ty/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ use tracing::debug;
use triomphe::{Arc, ThinArc};

use crate::{
FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, all_super_traits,
consteval::intern_const_ref,
db::{HirDatabase, InternedOpaqueTyId},
generics::{Generics, generics, trait_self_param_idx},
Expand Down Expand Up @@ -1624,11 +1624,16 @@ pub(crate) fn field_types_with_diagnostics_query<'db>(
(res, create_diagnostics(ctx.diagnostics))
}

/// 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.
///
/// This query exists only to be used when resolving short-hand associated types
/// like `T::Item`.
///
/// See the analogous query in rustc and its comment:
/// <https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46>
///
/// This is a query mostly to handle cycles somewhat gracefully; e.g. the
/// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but
/// these are fine: `T: Foo<U::Item>, U: Foo<()>`.
Expand All @@ -1652,7 +1657,7 @@ pub(crate) fn generic_predicates_for_param<'db>(
);

// we have to filter out all other predicates *first*, before attempting to lower them
let predicate = |pred: &_, ctx: &mut TyLoweringContext<'_, '_>| match pred {
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) };
Expand Down Expand Up @@ -1700,11 +1705,7 @@ pub(crate) fn generic_predicates_for_param<'db>(
return false;
};

rustc_type_ir::elaborate::supertrait_def_ids(interner, tr.into()).any(|tr| {
tr.0.trait_items(db).items.iter().any(|(name, item)| {
matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name
})
})
trait_or_supertrait_has_assoc_type(db, tr, assoc_name)
}
TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false,
}
Expand All @@ -1717,7 +1718,7 @@ pub(crate) fn generic_predicates_for_param<'db>(
{
ctx.store = maybe_parent_generics.store();
for pred in maybe_parent_generics.where_predicates() {
if predicate(pred, &mut ctx) {
if has_relevant_bound(pred, &mut ctx) {
predicates.extend(
ctx.lower_where_predicate(
pred,
Expand Down Expand Up @@ -1757,6 +1758,27 @@ pub(crate) fn generic_predicates_for_param_cycle_result(
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
}

#[inline]
pub(crate) fn type_alias_bounds<'db>(
db: &'db dyn HirDatabase,
Expand Down
48 changes: 48 additions & 0 deletions crates/hir-ty/src/tests/regression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2598,3 +2598,51 @@ trait ColumnLike {
"#,
);
}

#[test]
fn issue_21006_generic_predicates_for_param_supertrait_cycle() {
check_no_mismatches(
r#"
trait VCipherSuite {}

trait CipherSuite
where
OprfHash<Self>: Hash,
{
}

type Bar<CS: CipherSuite> = <CS::Baz as VCipherSuite>::Hash;

type OprfHash<CS: CipherSuite> = <CS::Baz as VCipherSuite>::Hash;

impl<CS: CipherSuite> Foo<CS> {
fn seal() {}
}
"#,
);
}

#[test]
fn issue_21006_self_assoc_trait() {
check_types(
r#"
trait Baz {
fn baz(&self);
}

trait Foo {
type Assoc;
}

trait Bar: Foo
where
Self::Assoc: Baz,
{
fn bar(v: Self::Assoc) {
let _ = v.baz();
// ^ ()
}
}
"#,
);
}