diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaElementFinder.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaElementFinder.kt index 2d3586b2fce..7e142730ecb 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaElementFinder.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaElementFinder.kt @@ -22,12 +22,14 @@ import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.classId import org.jetbrains.kotlin.fir.declarations.superConeTypes import org.jetbrains.kotlin.fir.resolve.FirProvider import org.jetbrains.kotlin.fir.resolve.directExpansionType import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.resolve.transformers.FirSupertypeResolverTransformer import org.jetbrains.kotlin.fir.service import org.jetbrains.kotlin.fir.symbols.StandardClassIds import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol @@ -94,14 +96,16 @@ class FirJavaElementFinder( firClass.typeParameters.map { Pair(it.name.asString(), arrayOf(CommonClassNames.JAVA_LANG_OBJECT)) } ) - if (firClass.superTypeRefs.any { it !is FirResolvedTypeRef }) { - firClass.setCallbackOnSupertypesComputed { - stub.addSupertypesReferencesLists(firClass, session) + if (firClass.supertypesComputationStatus != FirClassLikeDeclaration.SupertypesComputationStatus.COMPUTED) { + val firForSuperClassFile = firProvider.getFirClassifierContainerFile(classId) + FirSupertypeResolverTransformer().apply { + initFromFile(firForSuperClassFile) + transformRegularClass(firClass, null) } - } else { - stub.addSupertypesReferencesLists(firClass, session) } + stub.addSupertypesReferencesLists(firClass, session) + for (nestedClass in firClass.declarations.filterIsInstance()) { buildStub(nestedClass, stub) } @@ -133,6 +137,7 @@ private fun FirRegularClass.packFlags(): Int { } private fun PsiClassStubImpl<*>.addSupertypesReferencesLists(firRegularClass: FirRegularClass, session: FirSession) { + if (firRegularClass.supertypesComputationStatus == FirClassLikeDeclaration.SupertypesComputationStatus.COMPUTING) return require(firRegularClass.superTypeRefs.all { it is FirResolvedTypeRef }) { "Supertypes for light class $qualifiedName are being added too early" } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSupertypeResolverTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSupertypeResolverTransformer.kt index 28c8e8cf3e1..21ec91996d2 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSupertypeResolverTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSupertypeResolverTransformer.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.resolve.FirProvider import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider +import org.jetbrains.kotlin.fir.resolve.firSymbolProvider import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes import org.jetbrains.kotlin.fir.scopes.FirPosition @@ -26,14 +27,16 @@ import org.jetbrains.kotlin.name.ClassId class FirSupertypeResolverTransformer : FirAbstractTreeTransformer(phase = FirResolvePhase.SUPER_TYPES) { override lateinit var session: FirSession - private val currentlyComputing: MutableSet = mutableSetOf() - private val fullyComputed: MutableSet = mutableSetOf() private lateinit var file: FirFile override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult { + initFromFile(file) + return super.transformFile(file, data) + } + + fun initFromFile(file: FirFile) { session = file.fileSession this.file = file - return super.transformFile(file, data) } override fun transformRegularClass(regularClass: FirRegularClass, data: Nothing?): CompositeTransformResult { @@ -62,11 +65,13 @@ class FirSupertypeResolverTransformer : FirAbstractTreeTransformer(phase = FirRe } private fun resolveSupertypesOrExpansions(classLikeDeclaration: FirClassLikeDeclaration<*>): FirDeclaration { + if (classLikeDeclaration.areSupertypesComputed() || classLikeDeclaration.areSupertypesComputing()) { + return classLikeDeclaration + } + val classId = classLikeDeclaration.symbol.classId - if (classId in fullyComputed) return classLikeDeclaration - - val visitor = ResolveSuperTypesTask(session, classId, file, currentlyComputing, fullyComputed, classLikeDeclaration) + val visitor = ResolveSuperTypesTask(session, classId, file, classLikeDeclaration) file.accept(visitor, null).single return visitor.resultingClass @@ -76,8 +81,6 @@ class FirSupertypeResolverTransformer : FirAbstractTreeTransformer(phase = FirRe override val session: FirSession, private val requestedClassId: ClassId, file: FirFile, - private val currentlyComputing: MutableSet, - private val fullyComputed: MutableSet, private val knownFirClassLikeDeclaration: FirClassLikeDeclaration<*>? = null ) : FirAbstractTreeTransformerWithSuperTypes(phase = FirResolvePhase.SUPER_TYPES, reversedScopePriority = true) { @@ -92,14 +95,14 @@ class FirSupertypeResolverTransformer : FirAbstractTreeTransformer(phase = FirRe val classId = regularClass.classId if (!isOuterClass(classId, requestedClassId)) return regularClass.compose() val transformedClass = withScopeCleanup { - if (classId in fullyComputed) return@withScopeCleanup regularClass + if (regularClass.areSupertypesComputed() || regularClass.areSupertypesComputing()) return@withScopeCleanup regularClass regularClass.addTypeParametersScope() val transformer = FirSpecificTypeResolverTransformer(towerScope, FirPosition.SUPER_TYPE_OR_EXPANSION, session) val resolvedTypesRefs = regularClass.superTypeRefs.map { transformer.transformTypeRef(it, data).single } - val resultingTypeRefs = resolveLoops(classId, resolvedTypesRefs) + val resultingTypeRefs = resolveLoops(regularClass, classId, resolvedTypesRefs) regularClass.replaceSupertypes(resultingTypeRefs) } @@ -119,14 +122,14 @@ class FirSupertypeResolverTransformer : FirAbstractTreeTransformer(phase = FirRe override fun transformTypeAlias(typeAlias: FirTypeAlias, data: Nothing?): CompositeTransformResult { val classId = typeAlias.symbol.classId // nested type aliases - if (classId in fullyComputed || !typeAlias.matchesRequestedDeclaration()) return typeAlias.compose() + if (typeAlias.areSupertypesComputed() || !typeAlias.matchesRequestedDeclaration()) return typeAlias.compose() return withScopeCleanup { typeAlias.addTypeParametersScope() val transformer = FirSpecificTypeResolverTransformer(towerScope, FirPosition.SUPER_TYPE_OR_EXPANSION, session) val resolvedTypesRef = transformer.transformTypeRef(typeAlias.expandedTypeRef, data).single - val resultingTypeRef = resolveLoops(classId, listOf(resolvedTypesRef)).firstOrNull() + val resultingTypeRef = resolveLoops(typeAlias, classId, listOf(resolvedTypesRef)).firstOrNull() typeAlias.replaceExpandTypeRef(resultingTypeRef ?: resolvedTypesRef).also { resultingClass = it @@ -135,17 +138,18 @@ class FirSupertypeResolverTransformer : FirAbstractTreeTransformer(phase = FirRe } private fun resolveLoops( + firClassLikeDeclaration: FirClassLikeDeclaration<*>, classId: ClassId, resolvedTypesRefs: List ): List { - currentlyComputing.add(classId) + firClassLikeDeclaration.supertypesComputationStatus = FirClassLikeDeclaration.SupertypesComputationStatus.COMPUTING val resultingTypeRefs = mutableListOf() for (superTypeRef in resolvedTypesRefs) { val resolvedType = superTypeRef.coneTypeSafe() ?: continue val superTypeClassId = resolvedType.lookupTag.classId - if (superTypeClassId.outerClasses().any(currentlyComputing::contains)) { + if (superTypeClassId.outerClasses().any { it.areSupertypesComputing() }) { resultingTypeRefs.add( FirErrorTypeRefImpl(superTypeRef.psi, "Recursion detected: ${superTypeRef.render()}") ) @@ -170,19 +174,22 @@ class FirSupertypeResolverTransformer : FirAbstractTreeTransformer(phase = FirRe val firForSuperClassFile = provider.getFirClassifierContainerFile(superTypeClassId) ResolveSuperTypesTask( - sessionForSupertype, superTypeClassId, firForSuperClassFile, - currentlyComputing, fullyComputed + sessionForSupertype, superTypeClassId, firForSuperClassFile ).transformFile(firForSuperClassFile, null) } resultingTypeRefs.add(superTypeRef) } - fullyComputed.add(classId) - currentlyComputing.remove(classId) + firClassLikeDeclaration.supertypesComputationStatus = FirClassLikeDeclaration.SupertypesComputationStatus.COMPUTED return resultingTypeRefs } + private fun ClassId.areSupertypesComputing(): Boolean { + val fir = session.firSymbolProvider.getClassLikeSymbolByFqName(this)?.fir ?: return false + return fir.areSupertypesComputing() + } + private fun resolveNestedClassesSupertypes( regularClass: FirRegularClass, data: Nothing? @@ -212,3 +219,8 @@ private fun isOuterClass(outerCandidate: ClassId, innerCandidate: ClassId) = innerCandidate.outerClasses().any { outerCandidate == it } private fun ClassId.outerClasses() = generateSequence(this, ClassId::getOuterClassId) + +private fun FirClassLikeDeclaration<*>.areSupertypesComputed() = + supertypesComputationStatus == FirClassLikeDeclaration.SupertypesComputationStatus.COMPUTED +private fun FirClassLikeDeclaration<*>.areSupertypesComputing() = + supertypesComputationStatus == FirClassLikeDeclaration.SupertypesComputationStatus.COMPUTING diff --git a/compiler/fir/resolve/testData/diagnostics/j+k/KJKComplexHierarchyNestedLoop.kt b/compiler/fir/resolve/testData/diagnostics/j+k/KJKComplexHierarchyNestedLoop.kt new file mode 100644 index 00000000000..f14f163e111 --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/j+k/KJKComplexHierarchyNestedLoop.kt @@ -0,0 +1,33 @@ +// FILE: K1.kt +class K2: J1() { + class Q : Nested() + fun bar() { + foo() + baz() + + superClass() + superI() + } +} + +// FILE: J1.java +public class J1 extends K2() { + public class Nested {} + + void baz() {} +} + +// FILE: K2.kt +open class KFirst : SuperClass(), SuperI { + fun foo() { + } +} + +// FILE: K3.kt +abstract class SuperClass { + fun superClass() {} +} + +interface SuperI { + fun superI() {} +} diff --git a/compiler/fir/resolve/testData/diagnostics/j+k/KJKComplexHierarchyNestedLoop.txt b/compiler/fir/resolve/testData/diagnostics/j+k/KJKComplexHierarchyNestedLoop.txt new file mode 100644 index 00000000000..98e5964a80d --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/j+k/KJKComplexHierarchyNestedLoop.txt @@ -0,0 +1,46 @@ +FILE: K1.kt + public final class K2 : R|J1| { + public constructor(): R|K2| { + super() + } + + public final class Q { + public constructor(): R|K2.Q| { + super() + } + + } + + public final fun bar(): R|kotlin/Unit| { + #() + #() + #() + #() + } + + } +FILE: K2.kt + public open class KFirst : R|SuperClass|, R|SuperI| { + public constructor(): R|KFirst| { + super() + } + + public final fun foo(): R|kotlin/Unit| { + } + + } +FILE: K3.kt + public abstract class SuperClass : R|kotlin/Any| { + public constructor(): R|SuperClass| { + super() + } + + public final fun superClass(): R|kotlin/Unit| { + } + + } + public abstract interface SuperI : R|kotlin/Any| { + public open fun superI(): R|kotlin/Unit| { + } + + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index df9537d62bb..21ba746e28e 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -46,6 +46,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { runTest("compiler/fir/resolve/testData/diagnostics/j+k/KJKComplexHierarchy.kt"); } + @TestMetadata("KJKComplexHierarchyNestedLoop.kt") + public void testKJKComplexHierarchyNestedLoop() throws Exception { + runTest("compiler/fir/resolve/testData/diagnostics/j+k/KJKComplexHierarchyNestedLoop.kt"); + } + @TestMetadata("KJKComplexHierarchyWithNested.kt") public void testKJKComplexHierarchyWithNested() throws Exception { runTest("compiler/fir/resolve/testData/diagnostics/j+k/KJKComplexHierarchyWithNested.kt");