diff --git a/build-common/src/org/jetbrains/kotlin/incremental/AbstractIncrementalCache.kt b/build-common/src/org/jetbrains/kotlin/incremental/AbstractIncrementalCache.kt index 9da80f7ce23..6bea2078cd3 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/AbstractIncrementalCache.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/AbstractIncrementalCache.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.serialization.deserialization.getClassId import java.io.File import java.util.* +import kotlin.collections.HashSet /** * Incremental cache common for JVM and JS, ClassName type aware @@ -203,12 +204,34 @@ abstract class AbstractIncrementalCache( override fun getComplementaryFilesRecursive(dirtyFiles: Collection): Collection { val complementaryFiles = HashSet() val filesQueue = ArrayDeque(dirtyFiles) + + val processedClasses = HashSet() + val processedFiles = HashSet() + while (filesQueue.isNotEmpty()) { val file = filesQueue.pollFirst() - complementaryFilesMap[file].forEach { - if (complementaryFiles.add(it)) filesQueue.add(it) + if (processedFiles.contains(file)) { + continue } + processedFiles.add(file) + complementaryFilesMap[file].forEach { + if (complementaryFiles.add(it) && !processedFiles.contains(it)) filesQueue.add(it) + } + val classes2recompile = sourceToClassesMap.getFqNames(file) + classes2recompile.filter { !processedClasses.contains(it) }.forEach {class2recompile -> + processedClasses.add(class2recompile) + val sealedClasses = findSealedSupertypes(class2recompile, listOf(this)) + val allSubtypes = sealedClasses.flatMap { withSubtypes(it, listOf(this)) }.also { + // there could be only one sealed class in hierarchy + processedClasses.addAll(it) + } + val files2add = allSubtypes.mapNotNull { classFqNameToSourceMap[it] }.filter { !processedFiles.contains(it) } + filesQueue.addAll(files2add) + } + + } + complementaryFiles.addAll(processedFiles) complementaryFiles.removeAll(dirtyFiles) return complementaryFiles } diff --git a/build-common/src/org/jetbrains/kotlin/incremental/buildUtil.kt b/build-common/src/org/jetbrains/kotlin/incremental/buildUtil.kt index 87d45ab7bd4..90b1f5c0e7d 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/buildUtil.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/buildUtil.kt @@ -149,8 +149,7 @@ fun ChangesCollector.getDirtyData( val dirtyLookupSymbols = HashSet() val dirtyClassesFqNames = HashSet() - val sealedParents = HashMap>() - val notSealedParents = HashSet() + val sealedParents = HashSet() for (change in changes()) { reporter.reportVerbose { "Process $change" } @@ -177,34 +176,12 @@ fun ChangesCollector.getDirtyData( fqNames.mapTo(dirtyLookupSymbols) { LookupSymbol(SAM_LOOKUP_NAME.asString(), it.asString()) } } else if (change is ChangeInfo.ParentsChanged) { - fun FqName.isSealed(): Boolean { - if (notSealedParents.contains(this)) return false - if (sealedParents.containsKey(this)) return true - return isSealed(this, caches).also { sealed -> - if (sealed) { - sealedParents[this] = HashSet() - } else { - notSealedParents.add(this) - } - } - } change.parentsChanged.forEach { parent -> - if (parent.isSealed()) { - sealedParents.getOrPut(parent) { HashSet() }.add(change.fqName) - } + sealedParents.addAll(findSealedSupertypes(parent, caches)) } } } - - val forceRecompile = HashSet().apply { - addAll(sealedParents.keys) - //we should recompile all inheritors with parent sealed class: add known subtypes - addAll(sealedParents.keys.flatMap { withSubtypes(it, caches) }) - //we should recompile all inheritors with parent sealed class: add new subtypes - addAll(sealedParents.values.flatten()) - } - - return DirtyData(dirtyLookupSymbols, dirtyClassesFqNames, forceRecompile) + return DirtyData(dirtyLookupSymbols, dirtyClassesFqNames, sealedParents) } fun mapLookupSymbolsToFiles( @@ -251,7 +228,22 @@ fun mapClassesFqNamesToFiles( fun isSealed( fqName: FqName, caches: Iterable -): Boolean = caches.any { it.isSealed(fqName) ?: false } +): Boolean = caches.any { cache -> cache.isSealed(fqName) ?: false } + +/** + * Finds sealed supertypes of class in same module. + * This method should be used for processing freedomOsSealedClasses feature, because + * mutually declared list of sealed subclasses could be declared only in the same module. + */ +fun findSealedSupertypes( + fqName: FqName, + caches: Iterable +): Collection { + if (isSealed(fqName, caches)) { + return listOf(fqName) + } + return caches.flatMap { cache -> cache.getSupertypesOf(fqName).filter { cache.isSealed(it) ?: false }} +} fun withSubtypes( typeFqName: FqName, diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputer.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputer.kt index b7bbfde003f..361a78744b2 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputer.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputer.kt @@ -281,7 +281,7 @@ object ClasspathChangesComputer { val unmatchedLookupSymbols = dirtyData.dirtyLookupSymbols.toMutableSet().also { it.removeAll(changes.lookupSymbols.toSet()) } - val unmatchedFqNames = (dirtyData.dirtyClassesFqNames + dirtyData.dirtyClassesFqNamesForceRecompile).toMutableSet().also { + val unmatchedFqNames = (dirtyData.dirtyClassesFqNames).toMutableSet().also { it.removeAll(changes.fqNames.toSet()) }