JVM_IR: produce collection stubs in a stable order

This means not storing intermediate results in any HashSets.

 #KT-47411 Fixed
This commit is contained in:
pyos
2021-06-24 09:53:53 +02:00
committed by TeamCityServer
parent 6ae6209031
commit 88320cbb05
2 changed files with 23 additions and 34 deletions
@@ -313,20 +313,17 @@ internal class CollectionStubMethodLowering(val context: JvmBackendContext) : Cl
// Compute stubs that should be generated, compare based on signature // Compute stubs that should be generated, compare based on signature
private fun generateRelevantStubMethods(irClass: IrClass): List<IrSimpleFunction> { private fun generateRelevantStubMethods(irClass: IrClass): List<IrSimpleFunction> {
val classStubFuns = collectionStubComputer.stubsForCollectionClasses(irClass) val classStubFuns = collectionStubComputer.stubsForCollectionClasses(irClass)
.flatMap { createStubFuns(irClass, it) } .flatMap { it.createStubFuns(irClass) }
if (classStubFuns.isEmpty()) return classStubFuns
val superClassStubSignatures = computeStubsForSuperClasses(irClass) val alreadyPresent = computeStubsForSuperClasses(irClass)
.flatMap { createStubFuns(irClass, it) } .flatMap { it.createStubFuns(irClass) }
.mapTo(HashSet()) { it.toJvmSignature() } .mapTo(HashSet()) { it.toJvmSignature() }
return classStubFuns return classStubFuns.filter { alreadyPresent.add(it.toJvmSignature()) }
.filter { it.toJvmSignature() !in superClassStubSignatures }
.associateBy { it.toJvmSignature() }
.values.toList()
} }
private fun createStubFuns(irClass: IrClass, stubs: StubsForCollectionClass): List<IrSimpleFunction> { private fun StubsForCollectionClass.createStubFuns(irClass: IrClass): List<IrSimpleFunction> {
val (readOnlyClass, mutableClass, candidatesForStubs) = stubs
val substitutionMap = computeSubstitutionMap(readOnlyClass.owner, mutableClass.owner, irClass) val substitutionMap = computeSubstitutionMap(readOnlyClass.owner, mutableClass.owner, irClass)
return candidatesForStubs.map { function -> return candidatesForStubs.map { function ->
createStubMethod(function, irClass, substitutionMap) createStubMethod(function, irClass, substitutionMap)
@@ -349,7 +346,7 @@ internal class CollectionStubMethodLowering(val context: JvmBackendContext) : Cl
private fun computeStubsForSuperClass(superClass: IrClass): List<StubsForCollectionClass> { private fun computeStubsForSuperClass(superClass: IrClass): List<StubsForCollectionClass> {
val superClassStubs = collectionStubComputer.stubsForCollectionClasses(superClass) val superClassStubs = collectionStubComputer.stubsForCollectionClasses(superClass)
if (superClass.modality != Modality.ABSTRACT) return superClassStubs if (superClassStubs.isEmpty() || superClass.modality != Modality.ABSTRACT) return superClassStubs
// An abstract superclass might contain an abstract function declaration that effectively overrides a stub to be generated, // An abstract superclass might contain an abstract function declaration that effectively overrides a stub to be generated,
// and thus have no non-abstract stub. // and thus have no non-abstract stub.
@@ -362,19 +359,18 @@ internal class CollectionStubMethodLowering(val context: JvmBackendContext) : Cl
if (abstractFunsByNameAndArity.isEmpty()) return superClassStubs if (abstractFunsByNameAndArity.isEmpty()) return superClassStubs
return superClassStubs.map { return superClassStubs.map {
val (readOnlyClass, mutableClass, candidatesForStubs) = it
// NB here we should build a stub in substitution context of the given superclass. // NB here we should build a stub in substitution context of the given superclass.
// Resulting stub can be different from a stub created in substitution context of the "current" class // Resulting stub can be different from a stub created in substitution context of the "current" class
// in case of (partially) specialized generic superclass. // in case of (partially) specialized generic superclass.
val substitutionMap = computeSubstitutionMap(readOnlyClass.owner, mutableClass.owner, superClass) val substitutionMap = computeSubstitutionMap(it.readOnlyClass.owner, it.mutableClass.owner, superClass)
val filteredCandidates = candidatesForStubs.filter { candidateFun -> val filteredCandidates = it.candidatesForStubs.filter { candidateFun ->
val stubMethod = createStubMethod(candidateFun, superClass, substitutionMap) val stubMethod = createStubMethod(candidateFun, superClass, substitutionMap)
val stubNameAndArity = stubMethod.nameAndArity val stubNameAndArity = stubMethod.nameAndArity
abstractFunsByNameAndArity[stubNameAndArity].orEmpty().none { abstractFun -> abstractFunsByNameAndArity[stubNameAndArity].orEmpty().none { abstractFun ->
isEffectivelyOverriddenBy(stubMethod, abstractFun) isEffectivelyOverriddenBy(stubMethod, abstractFun)
} }
} }
FilteredStubsForCollectionClass(readOnlyClass, mutableClass, filteredCandidates) FilteredStubsForCollectionClass(it.readOnlyClass, it.mutableClass, filteredCandidates)
} }
} }
@@ -401,10 +397,6 @@ internal interface StubsForCollectionClass {
val candidatesForStubs: Collection<IrSimpleFunction> val candidatesForStubs: Collection<IrSimpleFunction>
} }
internal operator fun StubsForCollectionClass.component1() = readOnlyClass
internal operator fun StubsForCollectionClass.component2() = mutableClass
internal operator fun StubsForCollectionClass.component3() = candidatesForStubs
internal class CollectionStubComputer(val context: JvmBackendContext) { internal class CollectionStubComputer(val context: JvmBackendContext) {
private class LazyStubsForCollectionClass( private class LazyStubsForCollectionClass(
@@ -442,15 +434,14 @@ internal class CollectionStubComputer(val context: JvmBackendContext) {
// In the ideal world, it should be enough to check that // In the ideal world, it should be enough to check that
// the given member function 'f' from 'MC' doesn't override anything from 'C'. // the given member function 'f' from 'MC' doesn't override anything from 'C'.
mutableClass.functions mutableClass.owner.functions
.map { it.owner }
.filter { memberFun -> .filter { memberFun ->
!memberFun.isFakeOverride || !memberFun.isFakeOverride ||
(memberFun.modality == Modality.ABSTRACT && memberFun.overriddenSymbols.none { overriddenFun -> (memberFun.modality == Modality.ABSTRACT && memberFun.overriddenSymbols.none { overriddenFun ->
overriddenFun.owner.parentAsClass.symbol == readOnlyClass overriddenFun.owner.parentAsClass.symbol == readOnlyClass
}) })
} }
.toHashSet() .toList()
} }
} }
@@ -478,12 +469,11 @@ internal class CollectionStubComputer(val context: JvmBackendContext) {
private fun computeStubsForCollectionClasses(irClass: IrClass): List<StubsForCollectionClass> { private fun computeStubsForCollectionClasses(irClass: IrClass): List<StubsForCollectionClass> {
if (irClass.isFromJava()) return emptyList() if (irClass.isFromJava()) return emptyList()
val stubs = preComputedStubs.filter { (readOnlyClass, mutableClass) -> val stubs = preComputedStubs.filter {
!irClass.symbol.isSubtypeOfClass(mutableClass) && irClass.symbol.isStrictSubtypeOfClass(it.readOnlyClass) && !irClass.symbol.isSubtypeOfClass(it.mutableClass)
irClass.superTypes.any { it.isSubtypeOfClass(readOnlyClass) }
} }
return stubs.filter { (readOnlyClass) -> return stubs.filter {
stubs.none { readOnlyClass != it.readOnlyClass && it.readOnlyClass.isSubtypeOfClass(readOnlyClass) } stubs.none { other -> it.readOnlyClass != other.readOnlyClass && other.readOnlyClass.isSubtypeOfClass(it.readOnlyClass) }
} }
} }
} }
@@ -21,15 +21,14 @@ fun IrClassifierSymbol.superTypes(): List<IrType> = when (this) {
else -> emptyList() else -> emptyList()
} }
fun IrClassifierSymbol.isSubtypeOfClass(superClass: IrClassSymbol): Boolean { fun IrClassifierSymbol.isSubtypeOfClass(superClass: IrClassSymbol): Boolean =
if (FqNameEqualityChecker.areEqual(this, superClass)) return true FqNameEqualityChecker.areEqual(this, superClass) || isStrictSubtypeOfClass(superClass)
return superTypes().any { it.isSubtypeOfClass(superClass) }
}
fun IrType.isSubtypeOfClass(superClass: IrClassSymbol): Boolean { fun IrClassifierSymbol.isStrictSubtypeOfClass(superClass: IrClassSymbol): Boolean =
if (this !is IrSimpleType) return false superTypes().any { it.isSubtypeOfClass(superClass) }
return classifier.isSubtypeOfClass(superClass)
} fun IrType.isSubtypeOfClass(superClass: IrClassSymbol): Boolean =
this is IrSimpleType && classifier.isSubtypeOfClass(superClass)
fun IrType.isSubtypeOf(superType: IrType, irBuiltIns: IrBuiltIns): Boolean { fun IrType.isSubtypeOf(superType: IrType, irBuiltIns: IrBuiltIns): Boolean {
return AbstractTypeChecker.isSubtypeOf( return AbstractTypeChecker.isSubtypeOf(