Repository: rust-lang/rust-analyzer (Rust)

Issue:
Unknown type deduction when using associated type Self::Type in a trait impl
Rust-analyzer cannot deduce a an associated type in a trait impl when using ```Self::Type```, even if it can when using full syntax ```<Struct as Trait>::Type```.

```rust
trait T {
    type T1;

    fn f(var: Self::T1);
    fn g(var: Self::T1);
}

struct S;

impl T for S {
    type T1 = u32;

    fn f(var: Self::T1) {
        let _a = var;
    }

    fn g(var: <S as T>::T1) {
        let _a = var;
    }
}
```

In this example, variable ```var``` is correctly deduced in the ```g``` function, but not in the ```f``` function.

![screenshot](https://user-images.githubusercontent.com/39058530/74928468-daacfc80-53d9-11ea-8da6-50ae6a89e5ff.png)

I'm using the latest release of rust-analyzer (2020-02-17) with the latest version of the vscode extension.




Discussion:
Another precision: it still works if I put ```<Self as T>::T1``` instead of ```<S as T>::T1```.
Basically resolving unselected associated types on `Self` in an impl block isn't implemented yet. Writing `T::Type` without specifying the trait deduces the associated type you mean from the where clauses in scope, but it's only allowed for type parameters... and `Self`, which isn't a type parameter in impls. (It is in trait definitions.) It's just an alias for the type being impl'd. So we immediately resolve `Self` to `S` in your example, but while you can write `Self::T1`, you can't write `S::T1`.

Resolving `Self` happens here:
https://github.com/rust-analyzer/rust-analyzer/blob/db1bbb11fbe85a5230452359e80535a2169d0929/crates/ra_hir_ty/src/lower.rs#L291-L300

And selecting an associated type happens a bit later, in `from_type_relative_path` which calls `select_associated_type`. By then, we don't know anymore that the type was written as `Self`. So what we will have to do is add some special handling in the above section that handles `Self::T1` in a similar way to `from_type_relative_path`.

(A future alternative might be to 'lazily normalize' the `Self` alias; i.e. have an explicit representation of `Self` as a type that is different from the actual self type, so then we could handle this in the existing code path. Chalk may move to such a representation, but that's in the future.)



Patch:
diff --git a/crates/ra_hir_ty/src/infer/path.rs b/crates/ra_hir_ty/src/infer/path.rs
index 471d603423dc..c733b9e1d299 100644
--- a/crates/ra_hir_ty/src/infer/path.rs
+++ b/crates/ra_hir_ty/src/infer/path.rs
@@ -40,7 +40,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
             let ty = self.make_ty(type_ref);
             let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
             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(
                 ty,
                 &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 =
                     remaining_segments.take(remaining_segments.len() - 1);
                 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,
                     def,
                     resolved_segment,
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index 092977e93054..b96dc126cf9c 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -91,7 +91,14 @@ pub enum TypeParamLoweringMode {
 
 impl Ty {
     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::Tuple(inner) => {
                 let inner_tys: Arc<[Ty]> = inner.iter().map(|tr| Ty::from_hir(ctx, tr)).collect();
@@ -100,7 +107,11 @@ impl Ty {
                     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) => {
                 let inner_ty = Ty::from_hir(ctx, inner);
                 Ty::apply_one(TypeCtor::RawPtr(*mutability), inner_ty)
@@ -183,7 +194,8 @@ impl Ty {
                 }
             }
             TypeRef::Error => Ty::Unknown,
-        }
+        };
+        (ty, res)
     }
 
     /// 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(
         ctx: &TyLoweringContext<'_, impl HirDatabase>,
         ty: Ty,
+        // We need the original resolution to lower `Self::AssocTy` correctly
+        res: Option<TypeNs>,
         remaining_segments: PathSegments<'_>,
-    ) -> Ty {
+    ) -> (Ty, Option<TypeNs>) {
         if remaining_segments.len() == 1 {
             // resolve unselected assoc types
             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 {
             // FIXME report error (ambiguous associated type)
-            Ty::Unknown
+            (Ty::Unknown, None)
         } else {
-            ty
+            (ty, res)
         }
     }
 
@@ -236,14 +250,14 @@ impl Ty {
         resolution: TypeNs,
         resolved_segment: PathSegment<'_>,
         remaining_segments: PathSegments<'_>,
-    ) -> Ty {
+    ) -> (Ty, Option<TypeNs>) {
         let ty = match resolution {
             TypeNs::TraitId(trait_) => {
                 // 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 trait_ref =
                     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 associated_ty = associated_type_by_name_including_super_traits(
                         ctx.db,
@@ -269,6 +283,7 @@ impl Ty {
                 } else {
                     Ty::Dyn(Arc::new([GenericPredicate::Implemented(trait_ref)]))
                 };
+                return (ty, None);
             }
             TypeNs::GenericParam(param_id) => {
                 let generics =
@@ -306,22 +321,25 @@ impl Ty {
             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()),
             // 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)
         if let Some(type_ref) = path.type_anchor() {
-            let ty = Ty::from_hir(ctx, &type_ref);
-            return Ty::from_type_relative_path(ctx, ty, path.segments());
+            let (ty, res) = Ty::from_hir_ext(ctx, &type_ref);
+            return Ty::from_type_relative_path(ctx, ty, res, path.segments());
         }
         let (resolution, remaining_index) =
             match ctx.resolver.resolve_path_in_type_ns(ctx.db, path.mod_path()) {
                 Some(it) => it,
-                None => return Ty::Unknown,
+                None => return (Ty::Unknown, None),
             };
         let (resolved_segment, remaining_segments) = match remaining_index {
             None => (
@@ -336,31 +354,27 @@ impl Ty {
     fn select_associated_type(
         ctx: &TyLoweringContext<'_, impl HirDatabase>,
         self_ty: Ty,
+        res: Option<TypeNs>,
         segment: PathSegment<'_>,
     ) -> Ty {
-        let def = match ctx.resolver.generic_def() {
-            Some(def) => def,
-            None => return Ty::Unknown, // this can't actually happen
-        };
-        let param_id = match self_ty {
-            Ty::Placeholder(id) if ctx.type_param_mode == TypeParamLoweringMode::Placeholder => id,
-            Ty::Bound(idx) if ctx.type_param_mode == TypeParamLoweringMode::Variable => {
-                let generics = generics(ctx.db, def);
-                let param_id = if let Some((id, _)) = generics.iter().nth(idx as usize) {
-                    id
-                } else {
-                    return Ty::Unknown;
-                };
-                param_id
+        let traits_from_env: Vec<_> = match res {
+            Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) {
+                None => return Ty::Unknown,
+                Some(trait_ref) => vec![trait_ref.value.trait_],
+            },
+            Some(TypeNs::GenericParam(param_id)) => {
+                let predicates = ctx.db.generic_predicates_for_param(param_id);
+                predicates
+                    .iter()
+                    .filter_map(|pred| match &pred.value {
+                        GenericPredicate::Implemented(tr) => Some(tr.trait_),
+                        _ => None,
+                    })
+                    .collect()
             }
-            _ => return Ty::Unknown, // Error: Ambiguous associated type
+            _ => return Ty::Unknown,
         };
-        let predicates = ctx.db.generic_predicates_for_param(param_id);
-        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));
+        let traits = traits_from_env.into_iter().flat_map(|t| all_super_traits(ctx.db, t));
         for t in traits {
             if let Some(associated_ty) = ctx.db.trait_data(t).associated_type_by_name(&segment.name)
             {


Test patch:
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 547010b35199..f009a708c36f 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -1802,6 +1802,47 @@ fn test<T, U>() where T::Item: Trait2, T: Trait<U::Item>, U: Trait<()> {
     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]
 fn trait_impl_self_ty() {
     let t = type_at(