mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-02 22:54:58 +00:00
Resolve Self::AssocTy
in impls
To do this we need to carry around the original resolution a bit, because `Self` gets resolved to the actual type immediately, but you're not allowed to write the equivalent type in a projection. (I tried just comparing the projection base type with the impl self type, but that seemed too dirty.) This is basically how rustc does it as well. Fixes #3249.
This commit is contained in:
parent
ce7496ec22
commit
d17c5416af
3 changed files with 93 additions and 38 deletions
|
@ -40,7 +40,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
let ty = self.make_ty(type_ref);
|
let ty = self.make_ty(type_ref);
|
||||||
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
|
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
|
||||||
let ctx = crate::lower::TyLoweringContext::new(self.db, &resolver);
|
let ctx = crate::lower::TyLoweringContext::new(self.db, &resolver);
|
||||||
let ty = Ty::from_type_relative_path(&ctx, ty, remaining_segments_for_ty);
|
let (ty, _) = Ty::from_type_relative_path(&ctx, ty, None, remaining_segments_for_ty);
|
||||||
self.resolve_ty_assoc_item(
|
self.resolve_ty_assoc_item(
|
||||||
ty,
|
ty,
|
||||||
&path.segments().last().expect("path had at least one segment").name,
|
&path.segments().last().expect("path had at least one segment").name,
|
||||||
|
@ -115,7 +115,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
let remaining_segments_for_ty =
|
let remaining_segments_for_ty =
|
||||||
remaining_segments.take(remaining_segments.len() - 1);
|
remaining_segments.take(remaining_segments.len() - 1);
|
||||||
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
|
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
|
||||||
let ty = Ty::from_partly_resolved_hir_path(
|
let (ty, _) = Ty::from_partly_resolved_hir_path(
|
||||||
&ctx,
|
&ctx,
|
||||||
def,
|
def,
|
||||||
resolved_segment,
|
resolved_segment,
|
||||||
|
|
|
@ -91,7 +91,14 @@ pub enum TypeParamLoweringMode {
|
||||||
|
|
||||||
impl Ty {
|
impl Ty {
|
||||||
pub fn from_hir(ctx: &TyLoweringContext<'_, impl HirDatabase>, type_ref: &TypeRef) -> Self {
|
pub fn from_hir(ctx: &TyLoweringContext<'_, impl HirDatabase>, type_ref: &TypeRef) -> Self {
|
||||||
match type_ref {
|
Ty::from_hir_ext(ctx, type_ref).0
|
||||||
|
}
|
||||||
|
pub fn from_hir_ext(
|
||||||
|
ctx: &TyLoweringContext<'_, impl HirDatabase>,
|
||||||
|
type_ref: &TypeRef,
|
||||||
|
) -> (Self, Option<TypeNs>) {
|
||||||
|
let mut res = None;
|
||||||
|
let ty = match type_ref {
|
||||||
TypeRef::Never => Ty::simple(TypeCtor::Never),
|
TypeRef::Never => Ty::simple(TypeCtor::Never),
|
||||||
TypeRef::Tuple(inner) => {
|
TypeRef::Tuple(inner) => {
|
||||||
let inner_tys: Arc<[Ty]> = inner.iter().map(|tr| Ty::from_hir(ctx, tr)).collect();
|
let inner_tys: Arc<[Ty]> = inner.iter().map(|tr| Ty::from_hir(ctx, tr)).collect();
|
||||||
|
@ -100,7 +107,11 @@ impl Ty {
|
||||||
Substs(inner_tys),
|
Substs(inner_tys),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
TypeRef::Path(path) => Ty::from_hir_path(ctx, path),
|
TypeRef::Path(path) => {
|
||||||
|
let (ty, res_) = Ty::from_hir_path(ctx, path);
|
||||||
|
res = res_;
|
||||||
|
ty
|
||||||
|
}
|
||||||
TypeRef::RawPtr(inner, mutability) => {
|
TypeRef::RawPtr(inner, mutability) => {
|
||||||
let inner_ty = Ty::from_hir(ctx, inner);
|
let inner_ty = Ty::from_hir(ctx, inner);
|
||||||
Ty::apply_one(TypeCtor::RawPtr(*mutability), inner_ty)
|
Ty::apply_one(TypeCtor::RawPtr(*mutability), inner_ty)
|
||||||
|
@ -183,7 +194,8 @@ impl Ty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TypeRef::Error => Ty::Unknown,
|
TypeRef::Error => Ty::Unknown,
|
||||||
}
|
};
|
||||||
|
(ty, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is only for `generic_predicates_for_param`, where we can't just
|
/// This is only for `generic_predicates_for_param`, where we can't just
|
||||||
|
@ -217,17 +229,19 @@ impl Ty {
|
||||||
pub(crate) fn from_type_relative_path(
|
pub(crate) fn from_type_relative_path(
|
||||||
ctx: &TyLoweringContext<'_, impl HirDatabase>,
|
ctx: &TyLoweringContext<'_, impl HirDatabase>,
|
||||||
ty: Ty,
|
ty: Ty,
|
||||||
|
// We need the original resolution to lower `Self::AssocTy` correctly
|
||||||
|
res: Option<TypeNs>,
|
||||||
remaining_segments: PathSegments<'_>,
|
remaining_segments: PathSegments<'_>,
|
||||||
) -> Ty {
|
) -> (Ty, Option<TypeNs>) {
|
||||||
if remaining_segments.len() == 1 {
|
if remaining_segments.len() == 1 {
|
||||||
// resolve unselected assoc types
|
// resolve unselected assoc types
|
||||||
let segment = remaining_segments.first().unwrap();
|
let segment = remaining_segments.first().unwrap();
|
||||||
Ty::select_associated_type(ctx, ty, segment)
|
(Ty::select_associated_type(ctx, ty, res, segment), None)
|
||||||
} else if remaining_segments.len() > 1 {
|
} else if remaining_segments.len() > 1 {
|
||||||
// FIXME report error (ambiguous associated type)
|
// FIXME report error (ambiguous associated type)
|
||||||
Ty::Unknown
|
(Ty::Unknown, None)
|
||||||
} else {
|
} else {
|
||||||
ty
|
(ty, res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,14 +250,14 @@ impl Ty {
|
||||||
resolution: TypeNs,
|
resolution: TypeNs,
|
||||||
resolved_segment: PathSegment<'_>,
|
resolved_segment: PathSegment<'_>,
|
||||||
remaining_segments: PathSegments<'_>,
|
remaining_segments: PathSegments<'_>,
|
||||||
) -> Ty {
|
) -> (Ty, Option<TypeNs>) {
|
||||||
let ty = match resolution {
|
let ty = match resolution {
|
||||||
TypeNs::TraitId(trait_) => {
|
TypeNs::TraitId(trait_) => {
|
||||||
// if this is a bare dyn Trait, we'll directly put the required ^0 for the self type in there
|
// if this is a bare dyn Trait, we'll directly put the required ^0 for the self type in there
|
||||||
let self_ty = if remaining_segments.len() == 0 { Some(Ty::Bound(0)) } else { None };
|
let self_ty = if remaining_segments.len() == 0 { Some(Ty::Bound(0)) } else { None };
|
||||||
let trait_ref =
|
let trait_ref =
|
||||||
TraitRef::from_resolved_path(ctx, trait_, resolved_segment, self_ty);
|
TraitRef::from_resolved_path(ctx, trait_, resolved_segment, self_ty);
|
||||||
return if remaining_segments.len() == 1 {
|
let ty = if remaining_segments.len() == 1 {
|
||||||
let segment = remaining_segments.first().unwrap();
|
let segment = remaining_segments.first().unwrap();
|
||||||
let associated_ty = associated_type_by_name_including_super_traits(
|
let associated_ty = associated_type_by_name_including_super_traits(
|
||||||
ctx.db,
|
ctx.db,
|
||||||
|
@ -269,6 +283,7 @@ impl Ty {
|
||||||
} else {
|
} else {
|
||||||
Ty::Dyn(Arc::new([GenericPredicate::Implemented(trait_ref)]))
|
Ty::Dyn(Arc::new([GenericPredicate::Implemented(trait_ref)]))
|
||||||
};
|
};
|
||||||
|
return (ty, None);
|
||||||
}
|
}
|
||||||
TypeNs::GenericParam(param_id) => {
|
TypeNs::GenericParam(param_id) => {
|
||||||
let generics =
|
let generics =
|
||||||
|
@ -306,22 +321,25 @@ impl Ty {
|
||||||
TypeNs::BuiltinType(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()),
|
TypeNs::BuiltinType(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()),
|
||||||
TypeNs::TypeAliasId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()),
|
TypeNs::TypeAliasId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()),
|
||||||
// FIXME: report error
|
// FIXME: report error
|
||||||
TypeNs::EnumVariantId(_) => return Ty::Unknown,
|
TypeNs::EnumVariantId(_) => return (Ty::Unknown, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ty::from_type_relative_path(ctx, ty, remaining_segments)
|
Ty::from_type_relative_path(ctx, ty, Some(resolution), remaining_segments)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_hir_path(ctx: &TyLoweringContext<'_, impl HirDatabase>, path: &Path) -> Ty {
|
pub(crate) fn from_hir_path(
|
||||||
|
ctx: &TyLoweringContext<'_, impl HirDatabase>,
|
||||||
|
path: &Path,
|
||||||
|
) -> (Ty, Option<TypeNs>) {
|
||||||
// Resolve the path (in type namespace)
|
// Resolve the path (in type namespace)
|
||||||
if let Some(type_ref) = path.type_anchor() {
|
if let Some(type_ref) = path.type_anchor() {
|
||||||
let ty = Ty::from_hir(ctx, &type_ref);
|
let (ty, res) = Ty::from_hir_ext(ctx, &type_ref);
|
||||||
return Ty::from_type_relative_path(ctx, ty, path.segments());
|
return Ty::from_type_relative_path(ctx, ty, res, path.segments());
|
||||||
}
|
}
|
||||||
let (resolution, remaining_index) =
|
let (resolution, remaining_index) =
|
||||||
match ctx.resolver.resolve_path_in_type_ns(ctx.db, path.mod_path()) {
|
match ctx.resolver.resolve_path_in_type_ns(ctx.db, path.mod_path()) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return Ty::Unknown,
|
None => return (Ty::Unknown, None),
|
||||||
};
|
};
|
||||||
let (resolved_segment, remaining_segments) = match remaining_index {
|
let (resolved_segment, remaining_segments) = match remaining_index {
|
||||||
None => (
|
None => (
|
||||||
|
@ -336,31 +354,27 @@ impl Ty {
|
||||||
fn select_associated_type(
|
fn select_associated_type(
|
||||||
ctx: &TyLoweringContext<'_, impl HirDatabase>,
|
ctx: &TyLoweringContext<'_, impl HirDatabase>,
|
||||||
self_ty: Ty,
|
self_ty: Ty,
|
||||||
|
res: Option<TypeNs>,
|
||||||
segment: PathSegment<'_>,
|
segment: PathSegment<'_>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let def = match ctx.resolver.generic_def() {
|
let traits_from_env: Vec<_> = match res {
|
||||||
Some(def) => def,
|
Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) {
|
||||||
None => return Ty::Unknown, // this can't actually happen
|
None => return Ty::Unknown,
|
||||||
};
|
Some(trait_ref) => vec![trait_ref.value.trait_],
|
||||||
let param_id = match self_ty {
|
},
|
||||||
Ty::Placeholder(id) if ctx.type_param_mode == TypeParamLoweringMode::Placeholder => id,
|
Some(TypeNs::GenericParam(param_id)) => {
|
||||||
Ty::Bound(idx) if ctx.type_param_mode == TypeParamLoweringMode::Variable => {
|
let predicates = ctx.db.generic_predicates_for_param(param_id);
|
||||||
let generics = generics(ctx.db, def);
|
predicates
|
||||||
let param_id = if let Some((id, _)) = generics.iter().nth(idx as usize) {
|
.iter()
|
||||||
id
|
.filter_map(|pred| match &pred.value {
|
||||||
} else {
|
GenericPredicate::Implemented(tr) => Some(tr.trait_),
|
||||||
return Ty::Unknown;
|
_ => None,
|
||||||
};
|
})
|
||||||
param_id
|
.collect()
|
||||||
}
|
}
|
||||||
_ => return Ty::Unknown, // Error: Ambiguous associated type
|
_ => return Ty::Unknown,
|
||||||
};
|
};
|
||||||
let predicates = ctx.db.generic_predicates_for_param(param_id);
|
let traits = traits_from_env.into_iter().flat_map(|t| all_super_traits(ctx.db, t));
|
||||||
let traits_from_env = predicates.iter().filter_map(|pred| match &pred.value {
|
|
||||||
GenericPredicate::Implemented(tr) => Some(tr.trait_),
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
let traits = traits_from_env.flat_map(|t| all_super_traits(ctx.db, t));
|
|
||||||
for t in traits {
|
for t in traits {
|
||||||
if let Some(associated_ty) = ctx.db.trait_data(t).associated_type_by_name(&segment.name)
|
if let Some(associated_ty) = ctx.db.trait_data(t).associated_type_by_name(&segment.name)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1802,6 +1802,47 @@ fn test<T, U>() where T::Item: Trait2, T: Trait<U::Item>, U: Trait<()> {
|
||||||
assert_eq!(t, "u32");
|
assert_eq!(t, "u32");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unselected_projection_on_trait_self() {
|
||||||
|
assert_snapshot!(infer(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
trait Trait {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn f(&self, x: Self::Item);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
impl Trait for S {
|
||||||
|
type Item = u32;
|
||||||
|
fn f(&self, x: Self::Item) { let y = x; }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S2;
|
||||||
|
|
||||||
|
impl Trait for S2 {
|
||||||
|
type Item = i32;
|
||||||
|
fn f(&self, x: <Self>::Item) { let y = x; }
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
), @r###"
|
||||||
|
[54; 58) 'self': &Self
|
||||||
|
[60; 61) 'x': {unknown}
|
||||||
|
[140; 144) 'self': &S
|
||||||
|
[146; 147) 'x': u32
|
||||||
|
[161; 175) '{ let y = x; }': ()
|
||||||
|
[167; 168) 'y': u32
|
||||||
|
[171; 172) 'x': u32
|
||||||
|
[242; 246) 'self': &S2
|
||||||
|
[248; 249) 'x': i32
|
||||||
|
[265; 279) '{ let y = x; }': ()
|
||||||
|
[271; 272) 'y': i32
|
||||||
|
[275; 276) 'x': i32
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trait_impl_self_ty() {
|
fn trait_impl_self_ty() {
|
||||||
let t = type_at(
|
let t = type_at(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue