Auto merge of #15825 - Austaras:master, r=flodiebold
fix: better resolve assoc item with type bound Closes #15772
This commit is contained in:
commit
8d3522e557
5 changed files with 173 additions and 26 deletions
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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()`.
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue