Skip to content

Commit

Permalink
fix: better resolve assoc item with type bound
Browse files Browse the repository at this point in the history
  • Loading branch information
Austaras committed Nov 6, 2023
1 parent c1c9e10 commit 2e6f19e
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 23 deletions.
29 changes: 29 additions & 0 deletions crates/hir-ty/src/infer/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,35 @@ impl<'a> InferenceTable<'a> {
solution
}

pub(crate) fn try_resolve_alias(&mut self, goal: Goal) -> bool {
let in_env = InEnvironment::new(&self.trait_env.env, goal);
let canonicalized = self.canonicalize(in_env);
let solution = self.db.trait_solve(
self.trait_env.krate,
self.trait_env.block,
canonicalized.value.clone(),
);

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

pub(crate) fn register_obligation(&mut self, goal: Goal) {
let in_env = InEnvironment::new(&self.trait_env.env, goal);
self.register_obligation_in_env(in_env)
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
50 changes: 31 additions & 19 deletions crates/hir-ty/src/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1422,26 +1422,38 @@ 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 mut alias = Vec::new();
let mut other_predicate = Vec::new();

for predicate in predicates.iter() {
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);

if let WhereClause::AliasEq(_) = p {
alias.push(p);
} else {
other_predicate.push(p);
}
}

for p in alias {
if !table.try_resolve_alias(p.cast(Interner)) {
return IsValidCandidate::No;
}
}

for p in other_predicate {
if table.try_obligation(p.cast(Interner)).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
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 2e6f19e

Please sign in to comment.