[FIR] avoid jumping from super type transformer for local classes to unresolved non-local classes

In the worst case, we will visit the entire hierarchy of a local class,
where non-local classes are not yet resolved

^KT-56550
This commit is contained in:
Dmitrii Gridin
2023-05-07 15:48:10 +02:00
committed by Space Team
parent 6f584bcbe7
commit ee74e10f28
5 changed files with 495 additions and 9 deletions
@@ -43,6 +43,8 @@ import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.types.model.TypeArgumentMarker
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
class FirSupertypeResolverProcessor(session: FirSession, scopeSession: ScopeSession) : FirTransformerBasedResolveProcessor(
session, scopeSession, FirResolvePhase.SUPER_TYPES
@@ -81,7 +83,7 @@ fun <F : FirClassLikeDeclaration> F.runSupertypeResolvePhaseForLocalClass(
useSiteFile: FirFile,
containingDeclarations: List<FirDeclaration>,
): F {
val supertypeComputationSession = SupertypeComputationSession()
val supertypeComputationSession = SupertypeComputationSessionForLocalClasses()
val supertypeResolverVisitor = FirSupertypeResolverVisitor(
session, supertypeComputationSession, scopeSession,
currentScopeList.toPersistentList(),
@@ -97,6 +99,41 @@ fun <F : FirClassLikeDeclaration> F.runSupertypeResolvePhaseForLocalClass(
return this.transform<F, Nothing?>(applySupertypesTransformer, null)
}
/**
* We should resolve non-local classes to [FirResolvePhase.SUPER_TYPES] explicitly
* to avoid unsafe access to unresolved super type references
*
* Example:
* ```
* open class TopLevelClass
* open class AnotherTopLevelClass : TopLevelClass()
*
* fun resolveMe() {
* class LocalClass : AnotherTopLevelClass() {
* class NestedLocalClass
* }
* }
* ```
*
* During the resolution of local classes, in the best case, we will only try
* to access the unresolved super type reference of "AnotherTopLevelClass" that is unsafe in the context of parallel resolution.
* In the worst case, from NestedLocalClass we will visit the entire hierarchy of "LocalClass"
*/
private class SupertypeComputationSessionForLocalClasses : SupertypeComputationSession() {
override fun getResolvedSuperTypeRefsForOutOfSessionDeclaration(classLikeDeclaration: FirClassLikeDeclaration): List<FirResolvedTypeRef>? {
classLikeDeclaration.lazyResolveToPhase(FirResolvePhase.SUPER_TYPES)
return super.getResolvedSuperTypeRefsForOutOfSessionDeclaration(classLikeDeclaration)
}
override fun supertypeRefs(declaration: FirClassLikeDeclaration): List<FirTypeRef> {
if (!declaration.isLocal) {
declaration.lazyResolveToPhase(FirResolvePhase.SUPER_TYPES)
}
return super.supertypeRefs(declaration)
}
}
private class FirApplySupertypesTransformer(
private val supertypeComputationSession: SupertypeComputationSession,
private val session: FirSession,
@@ -285,16 +322,10 @@ class FirSupertypeResolverVisitor(
if (supertype !is ConeClassLikeType) continue
val supertypeModuleSession = supertype.toSymbol(session)?.moduleData?.session ?: continue
val fir = supertype.lookupTag.toSymbol(supertypeModuleSession)?.fir ?: continue
resolveAllSupertypes(fir, fir.supertypeRefs(), visited)
resolveAllSupertypes(fir, supertypeComputationSession.supertypeRefs(fir), visited)
}
}
private fun FirClassLikeDeclaration.supertypeRefs() = when (this) {
is FirRegularClass -> superTypeRefs
is FirTypeAlias -> listOf(expandedTypeRef)
else -> emptyList()
}
private fun prepareScopes(classLikeDeclaration: FirClassLikeDeclaration): PersistentList<FirScope> {
val classId = classLikeDeclaration.symbol.classId
val classModuleSession = classLikeDeclaration.moduleData.session
@@ -580,6 +611,12 @@ open class SupertypeComputationSession {
else -> null
}
internal open fun supertypeRefs(declaration: FirClassLikeDeclaration): List<FirTypeRef> = when (declaration) {
is FirRegularClass -> declaration.superTypeRefs
is FirTypeAlias -> listOf(declaration.expandedTypeRef)
else -> emptyList()
}
/**
* @param declaration declaration to be checked for loops
* @param visited visited declarations during the current loop search
@@ -725,7 +762,6 @@ open class SupertypeComputationSession {
}
}
sealed class SupertypeComputationStatus {
object NotComputed : SupertypeComputationStatus()
object Computing : SupertypeComputationStatus()