[IC] Fixed IC for when expressions involving sealed interfaces
#KT-45474 Fixed
This commit is contained in:
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.serialization.deserialization.getClassId
|
import org.jetbrains.kotlin.serialization.deserialization.getClassId
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.HashSet
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Incremental cache common for JVM and JS, ClassName type aware
|
* Incremental cache common for JVM and JS, ClassName type aware
|
||||||
@@ -203,12 +204,34 @@ abstract class AbstractIncrementalCache<ClassName>(
|
|||||||
override fun getComplementaryFilesRecursive(dirtyFiles: Collection<File>): Collection<File> {
|
override fun getComplementaryFilesRecursive(dirtyFiles: Collection<File>): Collection<File> {
|
||||||
val complementaryFiles = HashSet<File>()
|
val complementaryFiles = HashSet<File>()
|
||||||
val filesQueue = ArrayDeque(dirtyFiles)
|
val filesQueue = ArrayDeque(dirtyFiles)
|
||||||
|
|
||||||
|
val processedClasses = HashSet<FqName>()
|
||||||
|
val processedFiles = HashSet<File>()
|
||||||
|
|
||||||
while (filesQueue.isNotEmpty()) {
|
while (filesQueue.isNotEmpty()) {
|
||||||
val file = filesQueue.pollFirst()
|
val file = filesQueue.pollFirst()
|
||||||
complementaryFilesMap[file].forEach {
|
if (processedFiles.contains(file)) {
|
||||||
if (complementaryFiles.add(it)) filesQueue.add(it)
|
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)
|
complementaryFiles.removeAll(dirtyFiles)
|
||||||
return complementaryFiles
|
return complementaryFiles
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,8 +149,7 @@ fun ChangesCollector.getDirtyData(
|
|||||||
val dirtyLookupSymbols = HashSet<LookupSymbol>()
|
val dirtyLookupSymbols = HashSet<LookupSymbol>()
|
||||||
val dirtyClassesFqNames = HashSet<FqName>()
|
val dirtyClassesFqNames = HashSet<FqName>()
|
||||||
|
|
||||||
val sealedParents = HashMap<FqName, MutableSet<FqName>>()
|
val sealedParents = HashSet<FqName>()
|
||||||
val notSealedParents = HashSet<FqName>()
|
|
||||||
|
|
||||||
for (change in changes()) {
|
for (change in changes()) {
|
||||||
reporter.reportVerbose { "Process $change" }
|
reporter.reportVerbose { "Process $change" }
|
||||||
@@ -177,34 +176,12 @@ fun ChangesCollector.getDirtyData(
|
|||||||
|
|
||||||
fqNames.mapTo(dirtyLookupSymbols) { LookupSymbol(SAM_LOOKUP_NAME.asString(), it.asString()) }
|
fqNames.mapTo(dirtyLookupSymbols) { LookupSymbol(SAM_LOOKUP_NAME.asString(), it.asString()) }
|
||||||
} else if (change is ChangeInfo.ParentsChanged) {
|
} 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 ->
|
change.parentsChanged.forEach { parent ->
|
||||||
if (parent.isSealed()) {
|
sealedParents.addAll(findSealedSupertypes(parent, caches))
|
||||||
sealedParents.getOrPut(parent) { HashSet() }.add(change.fqName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return DirtyData(dirtyLookupSymbols, dirtyClassesFqNames, sealedParents)
|
||||||
val forceRecompile = HashSet<FqName>().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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun mapLookupSymbolsToFiles(
|
fun mapLookupSymbolsToFiles(
|
||||||
@@ -251,7 +228,22 @@ fun mapClassesFqNamesToFiles(
|
|||||||
fun isSealed(
|
fun isSealed(
|
||||||
fqName: FqName,
|
fqName: FqName,
|
||||||
caches: Iterable<IncrementalCacheCommon>
|
caches: Iterable<IncrementalCacheCommon>
|
||||||
): 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<IncrementalCacheCommon>
|
||||||
|
): Collection<FqName> {
|
||||||
|
if (isSealed(fqName, caches)) {
|
||||||
|
return listOf(fqName)
|
||||||
|
}
|
||||||
|
return caches.flatMap { cache -> cache.getSupertypesOf(fqName).filter { cache.isSealed(it) ?: false }}
|
||||||
|
}
|
||||||
|
|
||||||
fun withSubtypes(
|
fun withSubtypes(
|
||||||
typeFqName: FqName,
|
typeFqName: FqName,
|
||||||
|
|||||||
+1
-1
@@ -281,7 +281,7 @@ object ClasspathChangesComputer {
|
|||||||
val unmatchedLookupSymbols = dirtyData.dirtyLookupSymbols.toMutableSet().also {
|
val unmatchedLookupSymbols = dirtyData.dirtyLookupSymbols.toMutableSet().also {
|
||||||
it.removeAll(changes.lookupSymbols.toSet())
|
it.removeAll(changes.lookupSymbols.toSet())
|
||||||
}
|
}
|
||||||
val unmatchedFqNames = (dirtyData.dirtyClassesFqNames + dirtyData.dirtyClassesFqNamesForceRecompile).toMutableSet().also {
|
val unmatchedFqNames = (dirtyData.dirtyClassesFqNames).toMutableSet().also {
|
||||||
it.removeAll(changes.fqNames.toSet())
|
it.removeAll(changes.fqNames.toSet())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user