Repository: intellij-rust/intellij-rust
Issue: Unresolved associated type

## Environment

* **IntelliJ Rust plugin version:** 0.4.172.4665-221-nightly
* **Rust toolchain version:** 1.61.0 (fe5b13d68 2022-05-18) x86_64-unknown-linux-gnu
* **IDE name and version:** IntelliJ IDEA 2022.1 Ultimate Edition (IU-221.5080.210)
* **Operating system:** Linux 5.13.0-41-generic
* **Macro expansion engine:** new
* **Name resolution engine:** new
* **Additional experimental features:** org.rust.cargo.emulate.terminal

```rust
trait Foo { type Item; }

struct S<T>(T);
impl<T> S<T> {
    fn foo()
        where
            T: Foo,
            T::Item: ?Sized // T::Item is unresolved
    {}
}
```

Note that if I move the `T: Foo` bound to the `T`'s declaration, `T::Item` will be resolved as expected:

```rust
trait Foo { type Item; }

struct S<T>(T);
impl<T: Foo> S<T> {
    fn foo()
        where
            T::Item: ?Sized // ok
    {}
}
```

Changed files:
src/test/kotlin/org/rust/lang/core/resolve/RsTypeAwareGenericResolveTest.kt, src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt, src/main/kotlin/org/rust/lang/core/resolve/ImplLookup.kt, src/main/kotlin/org/rust/lang/core/psi/ext/RsTypeParameter.kt, src/main/kotlin/org/rust/lang/core/types/ty/TyTypeParameter.kt, src/main/kotlin/org/rust/lang/core/completion/RsLambdaExprCompletionProvider.kt, src/main/kotlin/org/rust/lang/core/resolve/ref/RsLitExprReferenceContributor.kt

diff --git a/src/main/kotlin/org/rust/lang/core/completion/RsLambdaExprCompletionProvider.kt b/src/main/kotlin/org/rust/lang/core/completion/RsLambdaExprCompletionProvider.kt
index f25c9812a..a467e2b42 100644
--- a/src/main/kotlin/org/rust/lang/core/completion/RsLambdaExprCompletionProvider.kt
+++ b/src/main/kotlin/org/rust/lang/core/completion/RsLambdaExprCompletionProvider.kt
@@ -28,6 +28,7 @@ import org.rust.lang.core.psiElement
 import org.rust.lang.core.types.expectedType
 import org.rust.lang.core.types.implLookup
 import org.rust.lang.core.types.ty.Ty
+import org.rust.lang.core.types.ty.TyTypeParameter
 import org.rust.openapiext.createSmartPointer
 
 object RsLambdaExprCompletionProvider : RsCompletionProvider() {
@@ -44,7 +45,12 @@ object RsLambdaExprCompletionProvider : RsCompletionProvider() {
         val element = parameters.position.safeGetOriginalOrSelf()
         val expr = element.parentOfType<RsExpr>() ?: return
         val exprExpectedType = expr.expectedType ?: return
-        val paramTypes = expr.implLookup.asTyFunction(exprExpectedType)?.value?.paramTypes ?: return
+        val lookup = if (exprExpectedType is TyTypeParameter && exprExpectedType.parameter is TyTypeParameter.Named) {
+            exprExpectedType.parameter.parameter.implLookup
+        } else {
+            expr.implLookup
+        }
+        val paramTypes = lookup.asTyFunction(exprExpectedType)?.value?.paramTypes ?: return
 
         val params = suggestedParams(paramTypes, expr)
         val start = if (expr.leftSiblings.filter { it !is PsiErrorElement }.firstOrNull()?.elementType == OR) {
diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsTypeParameter.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsTypeParameter.kt
index 2f92189d7..e117fb93a 100644
--- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsTypeParameter.kt
+++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsTypeParameter.kt
@@ -16,13 +16,15 @@ import org.rust.lang.core.stubs.RsTypeParameterStub
 import org.rust.lang.core.types.RsPsiTypeImplUtil
 import org.rust.lang.core.types.ty.Ty
 
+val RsTypeParameter.owner: RsGenericDeclaration?
+    get() = parent?.parent as? RsGenericDeclaration
+
 /**
  * Returns all bounds for type parameter.
  *
  * Don't use it for stub creation because it will cause [com.intellij.openapi.project.IndexNotReadyException]!
  */
 val RsTypeParameter.bounds: List<RsPolybound> get() {
-    val owner = parent?.parent as? RsGenericDeclaration
     val whereBounds =
         owner?.whereClause?.wherePredList.orEmpty()
             .filter { (it.typeReference?.skipParens() as? RsBaseType)?.path?.reference?.resolve() == this }
diff --git a/src/main/kotlin/org/rust/lang/core/resolve/ImplLookup.kt b/src/main/kotlin/org/rust/lang/core/resolve/ImplLookup.kt
index 566f580ac..8783916cf 100644
--- a/src/main/kotlin/org/rust/lang/core/resolve/ImplLookup.kt
+++ b/src/main/kotlin/org/rust/lang/core/resolve/ImplLookup.kt
@@ -26,6 +26,7 @@ import org.rust.openapiext.testAssert
 import org.rust.stdext.buildList
 import org.rust.stdext.exhaustive
 import org.rust.stdext.swapRemoveAt
+import org.rust.stdext.withPrevious
 import kotlin.LazyThreadSafetyMode.NONE
 import kotlin.LazyThreadSafetyMode.PUBLICATION
 
@@ -147,19 +148,20 @@ sealed class TraitImplSource {
  * Note: ParamEnv of an associated item (method) also contains bounds of its trait/impl
  * Note: callerBounds should have type `List<Predicate>` to also support lifetime bounds
  */
-data class ParamEnv(val callerBounds: List<TraitRef>) {
-    fun boundsFor(ty: Ty): Sequence<BoundElement<RsTraitItem>> =
-        callerBounds.asSequence().filter { it.selfTy.isEquivalentTo(ty) }.map { it.trait }
-
-    fun isEmpty(): Boolean = callerBounds.isEmpty()
+interface ParamEnv {
+    fun boundsFor(ty: Ty): Sequence<BoundElement<RsTraitItem>>
 
     companion object {
-        val EMPTY: ParamEnv = ParamEnv(emptyList())
-        val LEGACY: ParamEnv = ParamEnv(emptyList())
-
-        fun buildFor(decl: RsGenericDeclaration): ParamEnv {
-            val rawBounds = buildList<TraitRef> {
-                addAll(decl.bounds)
+        val EMPTY: ParamEnv = SimpleParamEnv(emptyList())
+
+        fun buildFor(decl: RsItemElement): ParamEnv {
+            val rawBounds = buildList {
+                if (decl is RsGenericDeclaration) {
+                    addAll(decl.bounds)
+                    if (decl is RsTraitItem) {
+                        add(TraitRef(TyTypeParameter.self(), decl.withDefaultSubst()))
+                    }
+                }
                 if (decl is RsAbstractable) {
                     when (val owner = decl.owner) {
                         is RsAbstractableOwner.Trait -> {
@@ -177,10 +179,10 @@ data class ParamEnv(val callerBounds: List<TraitRef>) {
 
             when (rawBounds.size) {
                 0 -> return EMPTY
-                1 -> return ParamEnv(rawBounds)
+                1 -> return SimpleParamEnv(rawBounds)
             }
 
-            val lookup = ImplLookup(decl.project, decl.containingCrate, decl.knownItems, ParamEnv(rawBounds))
+            val lookup = ImplLookup(decl.project, decl.containingCrate, decl.knownItems, SimpleParamEnv(rawBounds))
             val ctx = lookup.ctx
             val bounds2 = rawBounds.map {
                 val (bound, obligations) = ctx.normalizeAssociatedTypesIn(it)
@@ -189,7 +191,42 @@ data class ParamEnv(val callerBounds: List<TraitRef>) {
             }
             ctx.fulfill.selectWherePossible()
 
-            return ParamEnv(bounds2.map { ctx.fullyResolve(it) })
+            return SimpleParamEnv(bounds2.map { ctx.fullyResolve(it) })
+        }
+    }
+}
+
+class SimpleParamEnv(private val callerBounds: List<TraitRef>) : ParamEnv {
+    override fun boundsFor(ty: Ty): Sequence<BoundElement<RsTraitItem>> =
+        callerBounds.asSequence().filter { it.selfTy.isEquivalentTo(ty) }.map { it.trait }
+}
+
+class LazyParamEnv(private val parentItem: RsGenericDeclaration) : ParamEnv {
+    override fun boundsFor(ty: Ty): Sequence<BoundElement<RsTraitItem>> {
+        return if (ty is TyTypeParameter) {
+            val additionalBounds = when (val parameter = ty.parameter) {
+                is TyTypeParameter.Named -> if (parameter.parameter.owner != parentItem) {
+                    parentItem.whereClause?.wherePredList.orEmpty().asSequence()
+                        .filter { (it.typeReference?.skipParens() as? RsBaseType)?.path?.reference?.resolve() == parameter.parameter }
+                        .flatMap { it.typeParamBounds?.polyboundList.orEmpty() }
+                } else {
+                    emptySequence()
+                }
+                TyTypeParameter.Self -> if (parentItem !is RsTraitOrImpl) {
+                    parentItem.whereClause?.wherePredList.orEmpty().asSequence()
+                        .filter { (it.typeReference?.skipParens() as? RsBaseType)?.path?.kind == PathKind.CSELF }
+                        .flatMap { it.typeParamBounds?.polyboundList.orEmpty() }
+                } else {
+                    emptySequence()
+                }
+            }.mapNotNull {
+                if (it.hasQ) return@mapNotNull null // Ignore `T: ?Sized`
+                it.bound.traitRef?.resolveToBoundTrait()
+            }
+            @Suppress("DEPRECATION")
+            ty.getTraitBoundsTransitively().asSequence() + additionalBounds
+        } else {
+            emptySequence()
         }
     }
 }
@@ -228,10 +265,6 @@ class ImplLookup(
     }
 
     fun getEnvBoundTransitivelyFor(ty: Ty): Sequence<BoundElement<RsTraitItem>> {
-        if (paramEnv == ParamEnv.LEGACY && ty is TyTypeParameter) {
-            @Suppress("DEPRECATION")
-            return ty.getTraitBoundsTransitively().asSequence()
-        }
         return paramEnv.boundsFor(ty)
     }
 
@@ -1265,19 +1298,20 @@ class ImplLookup(
         fun relativeTo(psi: RsElement): ImplLookup {
             val parentItem = psi.contextOrSelf<RsItemElement>()
             val paramEnv = if (parentItem is RsGenericDeclaration) {
-                val useLegacy = psi.contextOrSelf<RsWherePred>() != null ||
-                    psi.contextOrSelf<RsBound>() != null ||
-                    run {
-                        val impl = psi.contextOrSelf<RsImplItem>() ?: return@run false
-                        impl.traitRef?.isAncestorOf(psi) == true || impl.typeReference?.isAncestorOf(psi) == true
-                    }
-                if (useLegacy) {
-                    // We should mock ParamEnv here. Otherwise, we run into infinite recursion
-                    // This is mostly a hack. It should be solved in the future somehow
-                    ParamEnv.LEGACY
+                val (ancestor, cameFrom) = psi.contexts.withPrevious().find { (it, _) ->
+                    it is RsWherePred || it is RsBound || it is RsImplItem
+                } ?: (null to null)
+                val isInTraitBoundOrImplSignature = ancestor != null && (ancestor !is RsImplItem
+                    || cameFrom is RsTraitRef || cameFrom is RsTypeReference)
+                if (isInTraitBoundOrImplSignature) {
+                    // We have to calculate type parameter bounds lazily in this case.
+                    // Otherwise, we run into infinite recursion
+                    LazyParamEnv(parentItem)
                 } else {
                     ParamEnv.buildFor(parentItem)
                 }
+            } else if (parentItem != null) {
+                ParamEnv.buildFor(parentItem)
             } else {
                 ParamEnv.EMPTY
             }
diff --git a/src/main/kotlin/org/rust/lang/core/resolve/ref/RsLitExprReferenceContributor.kt b/src/main/kotlin/org/rust/lang/core/resolve/ref/RsLitExprReferenceContributor.kt
index f343534b4..18480eed4 100644
--- a/src/main/kotlin/org/rust/lang/core/resolve/ref/RsLitExprReferenceContributor.kt
+++ b/src/main/kotlin/org/rust/lang/core/resolve/ref/RsLitExprReferenceContributor.kt
@@ -91,7 +91,7 @@ private fun isPathLitExpr(expr: RsLitExpr): Boolean {
     val (function, arguments) = getFunctionAndArguments(expr) ?: return false
     val owner = function.owner as? RsAbstractableOwner.Impl
     val ownerType = (owner?.impl?.typeReference?.normType as? TyAdt)?.item
-    val (implLookup, knownItems) = expr.implLookupAndKnownItems
+    val (implLookup, knownItems) = function.implLookupAndKnownItems
     val isCallExpr = arguments.parent is RsCallExpr
 
     return when {
diff --git a/src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt b/src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt
index d0d2a614c..eee07e926 100644
--- a/src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt
+++ b/src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt
@@ -35,7 +35,7 @@ import org.rust.stdext.RsResult.Ok
 
 fun inferTypesIn(element: RsInferenceContextOwner): RsInferenceResult {
     val items = element.knownItems
-    val paramEnv = if (element is RsGenericDeclaration) ParamEnv.buildFor(element) else ParamEnv.EMPTY
+    val paramEnv = if (element is RsItemElement) ParamEnv.buildFor(element) else ParamEnv.EMPTY
     val lookup = ImplLookup(element.project, element.containingCrate, items, paramEnv)
     return recursionGuard(element, { lookup.ctx.infer(element) }, memoize = false)
         ?: error("Can not run nested type inference")
diff --git a/src/main/kotlin/org/rust/lang/core/types/ty/TyTypeParameter.kt b/src/main/kotlin/org/rust/lang/core/types/ty/TyTypeParameter.kt
index d0a0b10cf..5c3b672bf 100644
--- a/src/main/kotlin/org/rust/lang/core/types/ty/TyTypeParameter.kt
+++ b/src/main/kotlin/org/rust/lang/core/types/ty/TyTypeParameter.kt
@@ -35,15 +35,15 @@ class TyTypeParameter private constructor(
 
     val name: String? get() = parameter.name
 
-    interface TypeParameter {
-        val name: String?
+    sealed class TypeParameter {
+        abstract val name: String?
     }
 
-    object Self : TypeParameter {
+    object Self : TypeParameter() {
         override val name: String get() = "Self"
     }
 
-    data class Named(val parameter: RsTypeParameter) : TypeParameter {
+    data class Named(val parameter: RsTypeParameter) : TypeParameter() {
         override val name: String? get() = parameter.name
     }
 
diff --git a/src/test/kotlin/org/rust/lang/core/resolve/RsTypeAwareGenericResolveTest.kt b/src/test/kotlin/org/rust/lang/core/resolve/RsTypeAwareGenericResolveTest.kt
index 078db9752..c4974b1e3 100644
--- a/src/test/kotlin/org/rust/lang/core/resolve/RsTypeAwareGenericResolveTest.kt
+++ b/src/test/kotlin/org/rust/lang/core/resolve/RsTypeAwareGenericResolveTest.kt
@@ -784,6 +784,17 @@ class RsTypeAwareGenericResolveTest : RsResolveTestBase() {
         }    //^
     """)
 
+    fun `test resolve assoc constant in assoc constant initializer`() = checkByCode("""
+        trait WithConst {
+            const C: i32;
+        }       //X
+
+        struct S<T>(T);
+        impl<T: WithConst> WithConst for S<T> {
+            const C: i32 = T::C;
+        }                   //^
+    """)
+
     fun `test assoc type in fn parameter`() = checkByCode("""
         pub trait Iter {
             type Item;
@@ -1423,4 +1434,17 @@ class RsTypeAwareGenericResolveTest : RsResolveTestBase() {
             W(1u8).foo();
         }        //^
     """)
+
+    fun `test resolve associated type in where predicate with type parameter declared on upper level`() = checkByCode("""
+        trait Foo { type Item; }
+                       //X
+        struct S<T>(T);
+        impl<T> S<T> {
+            fn foo()
+                where
+                    T: Foo,
+                    T::Item: ?Sized
+            {}       //^
+        }
+    """)
 }