Skip to content

Commit

Permalink
Auto merge of #15825 - Austaras:master, r=flodiebold
Browse files Browse the repository at this point in the history
fix: better resolve assoc item with type bound

Closes #15772
  • Loading branch information
bors committed Nov 21, 2023
2 parents 2e7e8cc + 808f668 commit 8d3522e
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 26 deletions.
2 changes: 1 addition & 1 deletion crates/hir-ty/src/infer/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ where
}

impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
pub(super) fn apply_solution(
pub(crate) fn apply_solution(
&self,
ctx: &mut InferenceTable<'_>,
solution: Canonical<Substitution>,
Expand Down
23 changes: 19 additions & 4 deletions crates/hir-ty/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1097,10 +1097,25 @@ impl<'a> TyLoweringContext<'a> {
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
);
if let Some(type_ref) = &binding.type_ref {
let ty = self.lower_ty(type_ref);
let alias_eq =
AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
if let (TypeRef::ImplTrait(bounds), ImplTraitLoweringState::Disallowed) =
(type_ref, &self.impl_trait_mode)
{
for bound in bounds {
predicates.extend(
self.lower_type_bound(
bound,
TyKind::Alias(AliasTy::Projection(projection_ty.clone()))
.intern(Interner),
false,
),
);
}
} else {
let ty = self.lower_ty(type_ref);
let alias_eq =
AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
}
}
for bound in binding.bounds.iter() {
predicates.extend(self.lower_type_bound(
Expand Down
69 changes: 48 additions & 21 deletions crates/hir-ty/src/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ use crate::{
primitive::{FloatTy, IntTy, UintTy},
static_lifetime, to_chalk_trait_id,
utils::all_super_traits,
AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, InEnvironment,
Interner, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt,
AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, Goal, Guidance,
InEnvironment, Interner, Scalar, Solution, Substitution, TraitEnvironment, TraitRef,
TraitRefExt, Ty, TyBuilder, TyExt,
};

/// This is used as a key for indexing impls.
Expand Down Expand Up @@ -1478,26 +1479,52 @@ fn is_valid_fn_candidate(
// We need to consider the bounds on the impl to distinguish functions of the same name
// for a type.
let predicates = db.generic_predicates(impl_id.into());
let valid = predicates
.iter()
.map(|predicate| {
let (p, b) = predicate
.clone()
.substitute(Interner, &impl_subst)
// Skipping the inner binders is ok, as we don't handle quantified where
// clauses yet.
.into_value_and_skipped_binders();
stdx::always!(b.len(Interner) == 0);
p
})
// It's ok to get ambiguity here, as we may not have enough information to prove
// obligations. We'll check if the user is calling the selected method properly
// later anyway.
.all(|p| table.try_obligation(p.cast(Interner)).is_some());
match valid {
true => IsValidCandidate::Yes,
false => IsValidCandidate::No,
let goals = predicates.iter().map(|p| {
let (p, b) = p
.clone()
.substitute(Interner, &impl_subst)
// Skipping the inner binders is ok, as we don't handle quantified where
// clauses yet.
.into_value_and_skipped_binders();
stdx::always!(b.len(Interner) == 0);

p.cast::<Goal>(Interner)
});

for goal in goals.clone() {
let in_env = InEnvironment::new(&table.trait_env.env, goal);
let canonicalized = table.canonicalize(in_env);
let solution = table.db.trait_solve(
table.trait_env.krate,
table.trait_env.block,
canonicalized.value.clone(),
);

match solution {
Some(Solution::Unique(canonical_subst)) => {
canonicalized.apply_solution(
table,
Canonical {
binders: canonical_subst.binders,
value: canonical_subst.value.subst,
},
);
}
Some(Solution::Ambig(Guidance::Definite(substs))) => {
canonicalized.apply_solution(table, substs);
}
Some(_) => (),
None => return IsValidCandidate::No,
}
}

for goal in goals {
if table.try_obligation(goal).is_none() {
return IsValidCandidate::No;
}
}

IsValidCandidate::Yes
} else {
// For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in
// `iterate_trait_method_candidates()`.
Expand Down
28 changes: 28 additions & 0 deletions crates/hir-ty/src/tests/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2597,6 +2597,34 @@ fn test<T: Trait>() {
);
}

#[test]
fn associated_type_in_type_bound() {
check_types(
r#"
//- minicore: deref
fn fb(f: Foo<&u8>) {
f.foobar();
//^^^^^^^^^^ u8
}
trait Bar {
fn bar(&self) -> u8;
}
impl Bar for u8 {
fn bar(&self) -> u8 { *self }
}
struct Foo<F> {
foo: F,
}
impl<F: core::ops::Deref<Target = impl Bar>> Foo<F> {
fn foobar(&self) -> u8 {
self.foo.deref().bar()
}
}
"#,
)
}

#[test]
fn dyn_trait_through_chalk() {
check_types(
Expand Down
77 changes: 77 additions & 0 deletions crates/ide-completion/src/completions/dot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1095,4 +1095,81 @@ fn test(s: S<Unknown>) {
"#]],
);
}

#[test]
fn assoc_impl_1() {
check(
r#"
//- minicore: deref
fn main() {
let foo: Foo<&u8> = Foo::new(&42_u8);
foo.$0
}
trait Bar {
fn bar(&self);
}
impl Bar for u8 {
fn bar(&self) {}
}
struct Foo<F> {
foo: F,
}
impl<F> Foo<F> {
fn new(foo: F) -> Foo<F> {
Foo { foo }
}
}
impl<F: core::ops::Deref<Target = impl Bar>> Foo<F> {
fn foobar(&self) {
self.foo.deref().bar()
}
}
"#,
expect![[r#"
fd foo &u8
me foobar() fn(&self)
"#]],
);
}

#[test]
fn assoc_impl_2() {
check(
r#"
//- minicore: deref
fn main() {
let foo: Foo<&u8> = Foo::new(&42_u8);
foo.$0
}
trait Bar {
fn bar(&self);
}
struct Foo<F> {
foo: F,
}
impl<F> Foo<F> {
fn new(foo: F) -> Foo<F> {
Foo { foo }
}
}
impl<B: Bar, F: core::ops::Deref<Target = B>> Foo<F> {
fn foobar(&self) {
self.foo.deref().bar()
}
}
"#,
expect![[r#"
fd foo &u8
"#]],
);
}
}

0 comments on commit 8d3522e

Please sign in to comment.