[LL FIR] LLFirSuperTypeTargetResolver: propagate looped types more accurately

'forceSkipResolvedClasses' is wrong here, because nested class (`CC`)
won't be marked as cycle but the outer is.
Instead of this, we should check
canHaveLoopInSupertypesHierarchy recursively

```kotlin
open class C : <!CYCLIC_INHERITANCE_HIERARCHY!>D<!>() {
    open class CC
}
open class D : <!CYCLIC_INHERITANCE_HIERARCHY!>C.CC<!>()
```

Also, this commit adds an optimization to avoid hierarchy search
for non-source classes (forceSkipResolvedClasses was introduced for this before)
This commit is contained in:
Dmitrii Gridin
2023-05-11 13:29:31 +02:00
committed by Space Team
parent 2b27fe4e65
commit 1d974b1ef7
@@ -188,7 +188,7 @@ private class LLFirSuperTypeTargetResolver(
}
private fun FirClassLikeDeclaration.asResolveTarget(): LLFirSingleResolveTarget? {
return takeIf { it.canHaveLoopInSupertypesHierarchy(session, forceSkipResolvedClasses = true) }
return takeIf { it.canHaveLoopInSupertypesHierarchy(session) }
?.tryCollectDesignationWithFile()
?.asResolveTarget()
}
@@ -253,45 +253,20 @@ private class LLFirSuperTypeTargetResolver(
* means that this class can't have loop with our class, because in this case this declaration will be present
* in the current supertypes resolve session
*/
private fun FirClassLikeDeclaration.canHaveLoopInSupertypesHierarchy(
session: FirSession,
forceSkipResolvedClasses: Boolean = false,
): Boolean = when {
private fun FirClassLikeDeclaration.canHaveLoopInSupertypesHierarchy(session: FirSession): Boolean = when {
this is FirJavaClass -> origin is FirDeclarationOrigin.Java.Source
origin !is FirDeclarationOrigin.Source -> false
resolvePhase < FirResolvePhase.SUPER_TYPES -> true
// We should still process resolved if it has loop in super type refs, because we can be part of this cycle
!forceSkipResolvedClasses && this is FirRegularClass -> hasLoopInSupertypeRefs(session)
!forceSkipResolvedClasses && this is FirTypeAlias -> hasLoopInSupertypeRefs(session)
else -> false
this is FirRegularClass && superTypeRefs.any(FirTypeRef::isLoopedSupertypeRef) -> true
this is FirTypeAlias && expandedTypeRef.isLoopedSupertypeRef -> true
else -> outerClass(session)?.canHaveLoopInSupertypesHierarchy(session) == true
}
private fun FirClassLikeDeclaration.outerClass(session: FirSession): FirRegularClass? = symbol.classId.parentClassId?.let { parentClassId ->
session.symbolProvider.getClassLikeSymbolByClassId(parentClassId)?.fir as? FirRegularClass
}
/**
* The class must be already resolved
*/
private fun FirRegularClass.hasLoopInSupertypeRefs(session: FirSession): Boolean {
if (superTypeRefs.any(FirTypeRef::isLoopedSupertypeRef)) {
return true
}
return outerClass(session)?.hasLoopInSupertypeRefs(session) == true
}
/**
* The typealias must be already resolved
*/
private fun FirTypeAlias.hasLoopInSupertypeRefs(session: FirSession): Boolean {
if (expandedTypeRef.isLoopedSupertypeRef) {
return true
}
return outerClass(session)?.hasLoopInSupertypeRefs(session) == true
}
private val FirTypeRef.isLoopedSupertypeRef: Boolean
get() {
if (this !is FirErrorTypeRef) return false