[LL FIR] rewrite LLFirSuperTypeTargetResolver

The previous implementation had issues with
locks, publication and performance

Also, this change fixed a problem with mixed cycle inheritance

^KT-56550
^KTIJ-25430 Fixed
^KTIJ-23520 Fixed
^KT-57623 Fixed
^KTIJ-25372 Fixed
^KT-58357 Fixed
This commit is contained in:
Dmitrii Gridin
2023-05-02 19:30:55 +02:00
committed by Space Team
parent 09d17d88d7
commit c2b09d3b1e
19 changed files with 1844 additions and 317 deletions
@@ -30,11 +30,13 @@ import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.resolve.transformers.resolveSupertypesInTheAir
import org.jetbrains.kotlin.fir.resolve.transformers.FirSupertypeResolverVisitor
import org.jetbrains.kotlin.fir.resolve.transformers.SupertypeComputationSession
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.*
@@ -113,7 +115,7 @@ class FirJavaElementFinder(
val superTypeRefs = when {
firClass.superTypeRefs.all { it is FirResolvedTypeRef } -> firClass.superTypeRefs
else -> firClass.resolveSupertypesInTheAir(session)
else -> firClass.resolveSupertypesOnAir(session)
}
stub.addSupertypesReferencesLists(firClass, superTypeRefs, session)
@@ -127,6 +129,13 @@ class FirJavaElementFinder(
}
private fun FirRegularClass.resolveSupertypesOnAir(session: FirSession): List<FirTypeRef> {
val visitor = FirSupertypeResolverVisitor(session, SupertypeComputationSession(), ScopeSession())
return visitor.withFile(session.firProvider.getFirClassifierContainerFile(this.symbol)) {
visitor.resolveSpecificClassLikeSupertypes(this, superTypeRefs)
}
}
private fun FirSession.collectAllDependentSourceSessions(): List<FirSession> {
val result = mutableListOf<FirSession>()
collectAllDependentSourceSessionsTo(result)
@@ -50,11 +50,11 @@ class FirSupertypeResolverProcessor(session: FirSession, scopeSession: ScopeSess
override val transformer = FirSupertypeResolverTransformer(session, scopeSession)
}
open class FirSupertypeResolverTransformer(
final override val session: FirSession,
scopeSession: ScopeSession
class FirSupertypeResolverTransformer(
override val session: FirSession,
scopeSession: ScopeSession,
) : FirAbstractPhaseTransformer<Any?>(FirResolvePhase.SUPER_TYPES) {
protected val supertypeComputationSession = SupertypeComputationSession()
private val supertypeComputationSession = SupertypeComputationSession()
private val supertypeResolverVisitor = FirSupertypeResolverVisitor(session, supertypeComputationSession, scopeSession)
private val applySupertypesTransformer = FirApplySupertypesTransformer(supertypeComputationSession, session, scopeSession)
@@ -97,18 +97,18 @@ fun <F : FirClassLikeDeclaration> F.runSupertypeResolvePhaseForLocalClass(
return this.transform<F, Nothing?>(applySupertypesTransformer, null)
}
open class FirApplySupertypesTransformer(
private class FirApplySupertypesTransformer(
private val supertypeComputationSession: SupertypeComputationSession,
private val session: FirSession,
private val scopeSession: ScopeSession
private val scopeSession: ScopeSession,
) : FirDefaultTransformer<Any?>() {
override fun <E : FirElement> transformElement(element: E, data: Any?): E {
return element
}
protected open fun transformDeclarationContent(declaration: FirDeclaration, data: Any?): FirDeclaration {
return declaration.transformChildren(this, null) as FirDeclaration
private fun transformDeclarationContent(declaration: FirDeclaration, data: Any?): FirDeclaration {
return declaration.transformChildren(this, data) as FirDeclaration
}
override fun transformFile(file: FirFile, data: Any?): FirFile {
@@ -200,15 +200,7 @@ private fun createOtherScopesForNestedClassesOrCompanion(
// See: prepareScopes()
}
fun FirRegularClass.resolveSupertypesInTheAir(session: FirSession): List<FirTypeRef> {
return FirSupertypeResolverVisitor(session, SupertypeComputationSession(), ScopeSession()).run {
withFile(session.firProvider.getFirClassifierContainerFile(this@resolveSupertypesInTheAir.symbol)) {
resolveSpecificClassLikeSupertypes(this@resolveSupertypesInTheAir, superTypeRefs)
}
}
}
open class FirSupertypeResolverVisitor(
class FirSupertypeResolverVisitor(
private val session: FirSession,
private val supertypeComputationSession: SupertypeComputationSession,
private val scopeSession: ScopeSession,
@@ -218,11 +210,14 @@ open class FirSupertypeResolverVisitor(
containingDeclarations: List<FirDeclaration> = emptyList(),
) : FirDefaultVisitor<Unit, Any?>() {
private val supertypeGenerationExtensions = session.extensionService.supertypeGenerators
private val classDeclarationsStack = ArrayDeque<FirClass>()
@PrivateForInline
val classDeclarationsStack = ArrayDeque<FirClass>()
init {
containingDeclarations.forEach {
if (it is FirClass) {
@OptIn(PrivateForInline::class)
classDeclarationsStack.add(it)
}
}
@@ -280,7 +275,7 @@ open class FirSupertypeResolverVisitor(
private fun resolveAllSupertypes(
classLikeDeclaration: FirClassLikeDeclaration,
supertypeRefs: List<FirTypeRef>,
visited: MutableSet<FirClassLikeDeclaration> = mutableSetOf()
visited: MutableSet<FirClassLikeDeclaration> = mutableSetOf(),
) {
if (!visited.add(classLikeDeclaration)) return
val supertypes: List<ConeKotlinType> =
@@ -333,8 +328,8 @@ open class FirSupertypeResolverVisitor(
private fun resolveSpecificClassLikeSupertypes(
classLikeDeclaration: FirClassLikeDeclaration,
resolveSuperTypeRefs: (FirTransformer<ScopeClassDeclaration>, ScopeClassDeclaration) -> List<FirResolvedTypeRef>
): List<FirTypeRef> {
resolveSuperTypeRefs: (FirTransformer<ScopeClassDeclaration>, ScopeClassDeclaration) -> List<FirResolvedTypeRef>,
): List<FirResolvedTypeRef> {
when (val status = supertypeComputationSession.getSupertypesComputationStatus(classLikeDeclaration)) {
is SupertypeComputationStatus.Computed -> return status.supertypeRefs
is SupertypeComputationStatus.Computing -> return listOf(
@@ -357,6 +352,7 @@ open class FirSupertypeResolverVisitor(
else session.firProvider.getFirClassifierContainerFileIfAny(classLikeDeclaration.symbol)
val resolvedTypesRefs = transformer.withFile(newUseSiteFile) {
@OptIn(PrivateForInline::class)
resolveSuperTypeRefs(
transformer,
ScopeClassDeclaration(scopes, classDeclarationsStack, containerDeclaration = classLikeDeclaration),
@@ -367,28 +363,38 @@ open class FirSupertypeResolverVisitor(
return resolvedTypesRefs
}
open fun visitDeclarationContent(declaration: FirDeclaration, data: Any?) {
declaration.acceptChildren(this, null)
private fun visitDeclarationContent(declaration: FirDeclaration, data: Any?) {
declaration.acceptChildren(this, data)
}
inline fun <T> withClass(firClass: FirClass, body: () -> T) {
@OptIn(PrivateForInline::class)
withClassDeclarationCleanup(classDeclarationsStack, firClass) {
body()
}
}
override fun visitRegularClass(regularClass: FirRegularClass, data: Any?) {
withClassDeclarationCleanup(classDeclarationsStack, regularClass) {
withClass(regularClass) {
resolveSpecificClassLikeSupertypes(regularClass, regularClass.superTypeRefs)
visitDeclarationContent(regularClass, null)
}
}
override fun visitAnonymousObject(anonymousObject: FirAnonymousObject, data: Any?) {
withClassDeclarationCleanup(classDeclarationsStack, anonymousObject) {
withClass(anonymousObject) {
resolveSpecificClassLikeSupertypes(anonymousObject, anonymousObject.superTypeRefs)
visitDeclarationContent(anonymousObject, null)
}
}
/**
* The function won't call supertypeRefs on classLikeDeclaration directly
*/
fun resolveSpecificClassLikeSupertypes(
classLikeDeclaration: FirClassLikeDeclaration,
supertypeRefs: List<FirTypeRef>
): List<FirTypeRef> {
supertypeRefs: List<FirTypeRef>,
): List<FirResolvedTypeRef> {
return resolveSpecificClassLikeSupertypes(classLikeDeclaration) { transformer, scopeDeclaration ->
if (!classLikeDeclaration.isLocalClassOrAnonymousObject()) {
session.lookupTracker?.let {
@@ -399,13 +405,8 @@ open class FirSupertypeResolverVisitor(
}
}
}
/*
This list is backed by mutable list and during iterating on it we can resolve supertypes of that class via IDE light classes
as IJ Java resolve may resolve a lot of stuff by light classes
this causes ConcurrentModificationException
So we create a copy of supertypeRefs to avoid it
*/
supertypeRefs.createCopy().mapTo(mutableListOf()) {
supertypeRefs.mapTo(mutableListOf()) {
val superTypeRef = it.transform<FirTypeRef, ScopeClassDeclaration>(transformer, scopeDeclaration)
val typeParameterType = superTypeRef.coneTypeSafe<ConeTypeParameterType>()
when {
@@ -429,13 +430,11 @@ open class FirSupertypeResolverVisitor(
}
}
private fun <T> List<T>.createCopy(): List<T> = ArrayList(this)
private fun addSupertypesFromExtensions(
klass: FirClassLikeDeclaration,
supertypeRefs: MutableList<FirResolvedTypeRef>,
typeResolveTransformer: FirTransformer<ScopeClassDeclaration>,
scopeDeclaration: ScopeClassDeclaration
scopeDeclaration: ScopeClassDeclaration,
) {
if (supertypeGenerationExtensions.isEmpty()) return
val typeResolveService = TypeResolveServiceForPlugins(typeResolveTransformer, scopeDeclaration)
@@ -450,7 +449,7 @@ open class FirSupertypeResolverVisitor(
private class TypeResolveServiceForPlugins(
val typeResolveTransformer: FirTransformer<ScopeClassDeclaration>,
val scopeDeclaration: ScopeClassDeclaration
val scopeDeclaration: ScopeClassDeclaration,
) : FirSupertypeGenerationExtension.TypeResolveService() {
override fun resolveUserType(type: FirUserTypeRef): FirResolvedTypeRef {
return type.transform(typeResolveTransformer, scopeDeclaration)
@@ -458,21 +457,24 @@ open class FirSupertypeResolverVisitor(
}
override fun visitTypeAlias(typeAlias: FirTypeAlias, data: Any?) {
resolveTypeAliasSupertype(typeAlias, typeAlias.expandedTypeRef)
}
fun resolveTypeAliasSupertype(typeAlias: FirTypeAlias, expandedTypeRef: FirTypeRef): List<FirResolvedTypeRef> {
// TODO: this if is a temporary hack for built-in types (because we can't load file for them)
if (typeAlias.expandedTypeRef is FirResolvedTypeRef) {
return
if (expandedTypeRef is FirResolvedTypeRef) {
return listOf(expandedTypeRef)
}
resolveSpecificClassLikeSupertypes(typeAlias) { transformer, scope ->
val resolvedTypeRef =
transformer.transformTypeRef(typeAlias.expandedTypeRef, scope) as? FirResolvedTypeRef
?: return@resolveSpecificClassLikeSupertypes listOf(
createErrorTypeRef(
typeAlias.expandedTypeRef,
"Unresolved expanded typeRef for ${typeAlias.symbol.classId}",
DiagnosticKind.UnresolvedExpandedType
)
return resolveSpecificClassLikeSupertypes(typeAlias) { transformer, scope ->
val resolvedTypeRef = transformer.transformTypeRef(expandedTypeRef, scope) as? FirResolvedTypeRef
?: return@resolveSpecificClassLikeSupertypes listOf(
createErrorTypeRef(
expandedTypeRef,
"Unresolved expanded typeRef for ${typeAlias.symbol.classId}",
DiagnosticKind.UnresolvedExpandedType
)
)
fun visitNestedTypeAliases(type: TypeArgumentMarker) {
if (type is ConeClassLikeType) {
@@ -505,11 +507,11 @@ private fun createErrorTypeRef(fir: FirElement, message: String, kind: Diagnosti
diagnostic = ConeSimpleDiagnostic(message, kind)
}
class SupertypeComputationSession {
open class SupertypeComputationSession {
private val fileScopesMap = hashMapOf<FirFile, ScopePersistentList>()
private val scopesForNestedClassesMap = hashMapOf<FirClass, ScopePersistentList>()
private val scopesForCompanionMap = hashMapOf<FirClass, ScopePersistentList>()
val supertypeStatusMap = linkedMapOf<FirClassLikeDeclaration, SupertypeComputationStatus>()
private val supertypeStatusMap = linkedMapOf<FirClassLikeDeclaration, SupertypeComputationStatus>()
val supertypesSupplier: SupertypeSupplier = object : SupertypeSupplier() {
override fun forClass(firClass: FirClass, useSiteSession: FirSession): List<ConeClassLikeType> {
@@ -558,52 +560,77 @@ class SupertypeComputationSession {
private val newClassifiersForBreakingLoops = mutableListOf<FirClassLikeDeclaration>()
fun breakLoops(session: FirSession) {
val visitedClassLikeDecls = mutableSetOf<FirClassLikeDeclaration>()
val loopedClassLikeDecls = mutableSetOf<FirClassLikeDeclaration>()
val path = mutableListOf<FirClassLikeDeclaration>()
val pathSet = mutableSetOf<FirClassLikeDeclaration>()
/**
* @return **true** if class is already resolved and can't be a part of loops
*/
protected open fun isAlreadyResolved(classLikeDeclaration: FirClassLikeDeclaration): Boolean = false
/**
* @param supertypeRefs a collection where at least one element is [FirErrorTypeRef] for looped references
*/
protected open fun reportLoopErrorRefs(classLikeDeclaration: FirClassLikeDeclaration, supertypeRefs: List<FirResolvedTypeRef>) {
supertypeStatusMap[classLikeDeclaration] = SupertypeComputationStatus.Computed(supertypeRefs)
}
protected open fun getResolvedSuperTypeRefsForOutOfSessionDeclaration(
classLikeDeclaration: FirClassLikeDeclaration,
): List<FirResolvedTypeRef>? = when (classLikeDeclaration) {
is FirRegularClass -> classLikeDeclaration.superTypeRefs.filterIsInstance<FirResolvedTypeRef>()
is FirTypeAlias -> listOfNotNull(classLikeDeclaration.expandedTypeRef as? FirResolvedTypeRef)
else -> null
}
/**
* @param declaration declaration to be checked for loops
* @param visited visited declarations during the current loop search
* @param looped declarations inside loop
*/
protected fun breakLoopFor(
declaration: FirClassLikeDeclaration,
session: FirSession,
visited: MutableSet<FirClassLikeDeclaration>, // always empty for LL FIR
looped: MutableSet<FirClassLikeDeclaration>, // always empty for LL FIR
pathSet: MutableSet<FirClassLikeDeclaration>,
path: MutableList<FirClassLikeDeclaration>,
) {
require(path.isEmpty()) { "Path should be empty" }
require(pathSet.isEmpty()) { "Path set should be empty" }
fun checkIsInLoop(
classLikeDecl: FirClassLikeDeclaration?,
classLikeDeclaration: FirClassLikeDeclaration?,
wasSubtypingInvolved: Boolean,
wereTypeArgumentsInvolved: Boolean,
) {
if (classLikeDecl == null) return
if (classLikeDeclaration == null || isAlreadyResolved(classLikeDeclaration)) return
require(!wasSubtypingInvolved || !wereTypeArgumentsInvolved) {
"This must hold by induction, because otherwise such a loop is allowed"
}
val supertypeRefs: List<FirResolvedTypeRef>
val supertypeComputationStatus = supertypeStatusMap[classLikeDecl]
supertypeRefs = if (supertypeComputationStatus != null) {
require(supertypeComputationStatus is SupertypeComputationStatus.Computed) {
"Expected computed supertypes in breakLoops for ${classLikeDecl.symbol.classId}"
val supertypeStatus = supertypeStatusMap[classLikeDeclaration]
val supertypeRefs: List<FirResolvedTypeRef> = if (supertypeStatus != null) {
require(supertypeStatus is SupertypeComputationStatus.Computed) {
"Expected computed supertypes in breakLoops for ${classLikeDeclaration.symbol.classId}"
}
supertypeComputationStatus.supertypeRefs
supertypeStatus.supertypeRefs
} else {
when (classLikeDecl) {
is FirRegularClass ->
classLikeDecl.superTypeRefs.filterIsInstance<FirResolvedTypeRef>()
is FirTypeAlias ->
(classLikeDecl.expandedTypeRef as? FirResolvedTypeRef)?.let { listOf(it) } ?: listOf()
else -> return
}
getResolvedSuperTypeRefsForOutOfSessionDeclaration(classLikeDeclaration) ?: return
}
if (classLikeDecl in visitedClassLikeDecls) {
if (classLikeDecl in pathSet) {
loopedClassLikeDecls.add(classLikeDecl)
loopedClassLikeDecls.addAll(path.takeLastWhile { element -> element != classLikeDecl })
if (classLikeDeclaration in visited) {
if (classLikeDeclaration in pathSet) {
looped.add(classLikeDeclaration)
looped.addAll(path.takeLastWhile { element -> element != classLikeDeclaration })
}
return
}
path.add(classLikeDecl)
pathSet.add(classLikeDecl)
visitedClassLikeDecls.add(classLikeDecl)
path.add(classLikeDeclaration)
pathSet.add(classLikeDeclaration)
visited.add(classLikeDeclaration)
val parentId = classLikeDecl.symbol.classId.relativeClassName.parent()
val parentId = classLikeDeclaration.symbol.classId.relativeClassName.parent()
if (!parentId.isRoot) {
val parentSymbol = session.symbolProvider.getClassLikeSymbolByClassId(ClassId.fromString(parentId.asString()))
if (parentSymbol is FirRegularClassSymbol) {
@@ -611,14 +638,14 @@ class SupertypeComputationSession {
}
}
val isTypeAlias = classLikeDecl is FirTypeAlias
val isTypeAlias = classLikeDeclaration is FirTypeAlias
val isSubtypingCurrentlyInvolved = !isTypeAlias
// This is an optimization that prevents collecting
// loops we don't want to report anyway.
if (wereTypeArgumentsInvolved && isSubtypingCurrentlyInvolved) {
path.removeAt(path.size - 1)
pathSet.remove(classLikeDecl)
pathSet.remove(classLikeDeclaration)
return
}
@@ -652,11 +679,11 @@ class SupertypeComputationSession {
}
resultSupertypeRefs.add(
if (classLikeDecl in loopedClassLikeDecls) {
if (classLikeDeclaration in looped) {
isErrorInSupertypesFound = true
createErrorTypeRef(
supertypeRef,
"Loop in supertype: ${classLikeDecl.symbol.classId} -> ${supertypeFir?.symbol?.classId}",
"Loop in supertype: ${classLikeDeclaration.symbol.classId} -> ${supertypeFir?.symbol?.classId}",
if (isTypeAlias) DiagnosticKind.RecursiveTypealiasExpansion else DiagnosticKind.LoopInSupertype
)
} else {
@@ -666,19 +693,34 @@ class SupertypeComputationSession {
}
if (isErrorInSupertypesFound) {
supertypeStatusMap[classLikeDecl] = SupertypeComputationStatus.Computed(resultSupertypeRefs)
reportLoopErrorRefs(classLikeDeclaration, resultSupertypeRefs)
}
path.removeAt(path.size - 1)
pathSet.remove(classLikeDecl)
pathSet.remove(classLikeDeclaration)
}
checkIsInLoop(declaration, wasSubtypingInvolved = false, wereTypeArgumentsInvolved = false)
require(path.isEmpty()) { "Path should be empty" }
}
fun breakLoops(session: FirSession) {
val visitedClassLikeDecls = mutableSetOf<FirClassLikeDeclaration>()
val loopedClassLikeDecls = mutableSetOf<FirClassLikeDeclaration>()
val path = mutableListOf<FirClassLikeDeclaration>()
val pathSet = mutableSetOf<FirClassLikeDeclaration>()
for (classifier in newClassifiersForBreakingLoops) {
checkIsInLoop(classifier, wasSubtypingInvolved = false, wereTypeArgumentsInvolved = false)
require(path.isEmpty()) {
"Path should be empty"
}
breakLoopFor(
declaration = classifier,
session = session,
visited = visitedClassLikeDecls,
looped = loopedClassLikeDecls,
pathSet = pathSet,
path = path,
)
}
newClassifiersForBreakingLoops.clear()
}
}
@@ -1,20 +0,0 @@
// LL_FIR_DIVERGENCE
// The compiler doesn't guarantee exhaustiveness in reporting of inheritance cycles, so the compiler and LL FIR results are equally valid.
// LL_FIR_DIVERGENCE
// FILE: I.kt
open class I : <!CYCLIC_INHERITANCE_HIERARCHY!>K<!>() {
fun foo() {}
}
// FILE: J.java
class J extends I {
void bar() {}
}
// FILE: K.kt
open class K : <!EXPOSED_SUPER_CLASS!>J<!>() {
fun baz() {}
}
@@ -1,3 +1,6 @@
// LL_FIR_DIVERGENCE
// The compiler doesn't guarantee exhaustiveness in reporting of inheritance cycles, so the compiler and LL FIR results are equally valid.
// LL_FIR_DIVERGENCE
// FILE: ExceptionTracker.kt
interface ExceptionTracker : <!CYCLIC_INHERITANCE_HIERARCHY!>LockBasedStorageManager.ExceptionHandlingStrategy<!> {