From 88320cbb05ceb65145e23f13a52e020cec410853 Mon Sep 17 00:00:00 2001 From: pyos Date: Thu, 24 Jun 2021 09:53:53 +0200 Subject: [PATCH] JVM_IR: produce collection stubs in a stable order This means not storing intermediate results in any HashSets. #KT-47411 Fixed --- .../jvm/lower/CollectionStubMethodLowering.kt | 42 +++++++------------ .../jetbrains/kotlin/ir/types/IrTypeUtils.kt | 15 ++++--- 2 files changed, 23 insertions(+), 34 deletions(-) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CollectionStubMethodLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CollectionStubMethodLowering.kt index ac316b17fde..abcc9a9ac6f 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CollectionStubMethodLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CollectionStubMethodLowering.kt @@ -313,20 +313,17 @@ internal class CollectionStubMethodLowering(val context: JvmBackendContext) : Cl // Compute stubs that should be generated, compare based on signature private fun generateRelevantStubMethods(irClass: IrClass): List { val classStubFuns = collectionStubComputer.stubsForCollectionClasses(irClass) - .flatMap { createStubFuns(irClass, it) } + .flatMap { it.createStubFuns(irClass) } + if (classStubFuns.isEmpty()) return classStubFuns - val superClassStubSignatures = computeStubsForSuperClasses(irClass) - .flatMap { createStubFuns(irClass, it) } + val alreadyPresent = computeStubsForSuperClasses(irClass) + .flatMap { it.createStubFuns(irClass) } .mapTo(HashSet()) { it.toJvmSignature() } - return classStubFuns - .filter { it.toJvmSignature() !in superClassStubSignatures } - .associateBy { it.toJvmSignature() } - .values.toList() + return classStubFuns.filter { alreadyPresent.add(it.toJvmSignature()) } } - private fun createStubFuns(irClass: IrClass, stubs: StubsForCollectionClass): List { - val (readOnlyClass, mutableClass, candidatesForStubs) = stubs + private fun StubsForCollectionClass.createStubFuns(irClass: IrClass): List { val substitutionMap = computeSubstitutionMap(readOnlyClass.owner, mutableClass.owner, irClass) return candidatesForStubs.map { function -> createStubMethod(function, irClass, substitutionMap) @@ -349,7 +346,7 @@ internal class CollectionStubMethodLowering(val context: JvmBackendContext) : Cl private fun computeStubsForSuperClass(superClass: IrClass): List { 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, // and thus have no non-abstract stub. @@ -362,19 +359,18 @@ internal class CollectionStubMethodLowering(val context: JvmBackendContext) : Cl if (abstractFunsByNameAndArity.isEmpty()) return superClassStubs return superClassStubs.map { - val (readOnlyClass, mutableClass, candidatesForStubs) = it // 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 // in case of (partially) specialized generic superclass. - val substitutionMap = computeSubstitutionMap(readOnlyClass.owner, mutableClass.owner, superClass) - val filteredCandidates = candidatesForStubs.filter { candidateFun -> + val substitutionMap = computeSubstitutionMap(it.readOnlyClass.owner, it.mutableClass.owner, superClass) + val filteredCandidates = it.candidatesForStubs.filter { candidateFun -> val stubMethod = createStubMethod(candidateFun, superClass, substitutionMap) val stubNameAndArity = stubMethod.nameAndArity abstractFunsByNameAndArity[stubNameAndArity].orEmpty().none { abstractFun -> isEffectivelyOverriddenBy(stubMethod, abstractFun) } } - FilteredStubsForCollectionClass(readOnlyClass, mutableClass, filteredCandidates) + FilteredStubsForCollectionClass(it.readOnlyClass, it.mutableClass, filteredCandidates) } } @@ -401,10 +397,6 @@ internal interface StubsForCollectionClass { val candidatesForStubs: Collection } -internal operator fun StubsForCollectionClass.component1() = readOnlyClass -internal operator fun StubsForCollectionClass.component2() = mutableClass -internal operator fun StubsForCollectionClass.component3() = candidatesForStubs - internal class CollectionStubComputer(val context: JvmBackendContext) { private class LazyStubsForCollectionClass( @@ -442,15 +434,14 @@ internal class CollectionStubComputer(val context: JvmBackendContext) { // In the ideal world, it should be enough to check that // the given member function 'f' from 'MC' doesn't override anything from 'C'. - mutableClass.functions - .map { it.owner } + mutableClass.owner.functions .filter { memberFun -> !memberFun.isFakeOverride || (memberFun.modality == Modality.ABSTRACT && memberFun.overriddenSymbols.none { overriddenFun -> overriddenFun.owner.parentAsClass.symbol == readOnlyClass }) } - .toHashSet() + .toList() } } @@ -478,12 +469,11 @@ internal class CollectionStubComputer(val context: JvmBackendContext) { private fun computeStubsForCollectionClasses(irClass: IrClass): List { if (irClass.isFromJava()) return emptyList() - val stubs = preComputedStubs.filter { (readOnlyClass, mutableClass) -> - !irClass.symbol.isSubtypeOfClass(mutableClass) && - irClass.superTypes.any { it.isSubtypeOfClass(readOnlyClass) } + val stubs = preComputedStubs.filter { + irClass.symbol.isStrictSubtypeOfClass(it.readOnlyClass) && !irClass.symbol.isSubtypeOfClass(it.mutableClass) } - return stubs.filter { (readOnlyClass) -> - stubs.none { readOnlyClass != it.readOnlyClass && it.readOnlyClass.isSubtypeOfClass(readOnlyClass) } + return stubs.filter { + stubs.none { other -> it.readOnlyClass != other.readOnlyClass && other.readOnlyClass.isSubtypeOfClass(it.readOnlyClass) } } } } \ No newline at end of file diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeUtils.kt index 13427724dcc..5d3f450bff7 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeUtils.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeUtils.kt @@ -21,15 +21,14 @@ fun IrClassifierSymbol.superTypes(): List = when (this) { else -> emptyList() } -fun IrClassifierSymbol.isSubtypeOfClass(superClass: IrClassSymbol): Boolean { - if (FqNameEqualityChecker.areEqual(this, superClass)) return true - return superTypes().any { it.isSubtypeOfClass(superClass) } -} +fun IrClassifierSymbol.isSubtypeOfClass(superClass: IrClassSymbol): Boolean = + FqNameEqualityChecker.areEqual(this, superClass) || isStrictSubtypeOfClass(superClass) -fun IrType.isSubtypeOfClass(superClass: IrClassSymbol): Boolean { - if (this !is IrSimpleType) return false - return classifier.isSubtypeOfClass(superClass) -} +fun IrClassifierSymbol.isStrictSubtypeOfClass(superClass: IrClassSymbol): Boolean = + superTypes().any { it.isSubtypeOfClass(superClass) } + +fun IrType.isSubtypeOfClass(superClass: IrClassSymbol): Boolean = + this is IrSimpleType && classifier.isSubtypeOfClass(superClass) fun IrType.isSubtypeOf(superType: IrType, irBuiltIns: IrBuiltIns): Boolean { return AbstractTypeChecker.isSubtypeOf(