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:
+16
-26
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user