diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/RecursionMarkers.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/RecursionMarkers.kt index 61f6c34633e..72e75dbadb6 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/RecursionMarkers.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/RecursionMarkers.kt @@ -25,9 +25,12 @@ object CirClassRecursionMarker : CirClass, CirRecursionMarker { set(_) = unsupported() } -object CirClassifierRecursionMarker : CirClassifier, CirRecursionMarker { +object CirTypeAliasRecursionMarker : CirTypeAlias, CirRecursionMarker { + override val underlyingType: CirClassOrTypeAliasType get() = unsupported() + override val expandedType: CirClassType get() = unsupported() override val annotations get() = unsupported() override val name get() = unsupported() override val typeParameters get() = unsupported() override val visibility get() = unsupported() + override val isLiftedUp: Boolean get() = unsupported() } diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/ClassOrTypeAliasTypeCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/ClassOrTypeAliasTypeCommonizer.kt new file mode 100644 index 00000000000..2329cd376ca --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/ClassOrTypeAliasTypeCommonizer.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.commonizer.core + +import org.jetbrains.kotlin.commonizer.cir.CirClassOrTypeAliasType +import org.jetbrains.kotlin.commonizer.cir.CirClassType +import org.jetbrains.kotlin.commonizer.cir.CirTypeAliasType +import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers + +internal class ClassOrTypeAliasTypeStatelessCommonizer( + private val classifiers: CirKnownClassifiers +) : StatelessCommonizer { + + override fun commonize(values: List): CirClassOrTypeAliasType? { + if (values.isEmpty()) return null + values.singleOrNull()?.let { return it } + + val classTypes = values.filterIsInstance() + val typeAliasTypes = values.filterIsInstance() + val expandedTypeAliasTypes = typeAliasTypes.map { it.expandedType() } + + if (values.all { it is CirClassType }) { + return ClassTypeCommonizer(classifiers).commonize(classTypes) + } + + if (values.all { it is CirTypeAliasType }) { + /* + In case regular type-alias-type commonization fails, we try to expand all type-aliases and + try our luck with commonizing those class types + */ + return TypeAliasTypeCommonizer(classifiers).commonize(typeAliasTypes) + ?: ClassTypeCommonizer(classifiers).commonize(expandedTypeAliasTypes) + } + + /* + There are type-alias types & class types enqueued for commonization. + We reduce all classes to a common representation and all type aliases to a common representation. + */ + val commonizedClass = ClassTypeCommonizer(classifiers).commonize(classTypes) ?: return null + val commonizedTypeAlias = TypeAliasTypeCommonizer(classifiers).commonize(typeAliasTypes) + + if (commonizedTypeAlias != null) { + return ClassTypeCommonizer(classifiers).commonize(listOf(commonizedClass, commonizedTypeAlias.expandedType())) + } + + /* + If type-alias type commonization failed: + Last attempt: Try to commonize all type-alias expansions with the commonized class + */ + return ClassTypeCommonizer(classifiers).commonize(expandedTypeAliasTypes + commonizedClass) + } +} + +internal class ClassOrTypeAliasTypeCommonizer(classifiers: CirKnownClassifiers) : + StatelessCommonizerAdapter(ClassOrTypeAliasTypeStatelessCommonizer(classifiers)) + +internal tailrec fun CirClassOrTypeAliasType.expandedType(): CirClassType = when (this) { + is CirClassType -> this + is CirTypeAliasType -> this.underlyingType.expandedType() +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/Commonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/Commonizer.kt index 0f3048b1f28..4cac2958606 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/Commonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/Commonizer.kt @@ -10,6 +10,16 @@ interface Commonizer { fun commonizeWith(next: T): Boolean } +fun Commonizer.commonize(values: List): R? { + /* Fast path: If commonizer supports StatelessCommonizer interface */ + if (this is StatelessCommonizerAdapter) { + return commonize(values) + } + + values.forEach { value -> if (!commonizeWith(value)) return null } + return result +} + abstract class AbstractStandardCommonizer : Commonizer { private enum class State { EMPTY, diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/StatelessCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/StatelessCommonizer.kt new file mode 100644 index 00000000000..2d2530ecda5 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/StatelessCommonizer.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.commonizer.core + +interface StatelessCommonizer { + fun commonize(values: List): R? +} + +open class StatelessCommonizerAdapter( + private val commonizer: StatelessCommonizer +) : AbstractStandardCommonizer(), StatelessCommonizer by commonizer { + private val values = mutableListOf() + + override fun commonizationResult(): R { + try { + return checkNotNull(commonize(values.toList())) + } finally { + values.clear() + } + } + + override fun initialize(first: T) = Unit + + override fun doCommonizeWith(next: T): Boolean { + values.add(next) + return commonize(values.toList()) != null + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeAliasCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeAliasCommonizer.kt index 2e3ba614970..3c6fd0c58f2 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeAliasCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeAliasCommonizer.kt @@ -17,11 +17,11 @@ import org.jetbrains.kotlin.descriptors.Modality * Secondary (less optimistic) branch: * - Make sure that all TAs are identical, so the resulting TA can be lifted up into "common" fragment. */ -class TypeAliasCommonizer(classifiers: CirKnownClassifiers) : AbstractStandardCommonizer() { +class TypeAliasCommonizer(classifiers: CirKnownClassifiers) : AbstractStandardCommonizer() { private val primary = TypeAliasShortCircuitingCommonizer(classifiers) private val secondary = TypeAliasLiftingUpCommonizer(classifiers) - override fun commonizationResult(): CirClassifier = primary.resultOrNull ?: secondary.result + override fun commonizationResult(): CirTypeAlias = primary.resultOrNull ?: secondary.result override fun initialize(first: CirTypeAlias) = Unit diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeCommonizer.kt index 4a6e07823e1..695ad52a327 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeCommonizer.kt @@ -14,61 +14,30 @@ import org.jetbrains.kotlin.commonizer.utils.isUnderKotlinNativeSyntheticPackage import org.jetbrains.kotlin.descriptors.Visibility class TypeCommonizer(private val classifiers: CirKnownClassifiers) : AbstractStandardCommonizer() { - private var typeCommonizer: AbstractStandardCommonizer<*, CirType>? = null - private var underlyingTypeCommonizer: TypeCommonizer? = null + private lateinit var typeCommonizer: Commonizer<*, CirType> - override fun commonizationResult() = typeCommonizer?.resultOrNull - ?: underlyingTypeCommonizer?.result - ?: typeCommonizer?.result - ?: failInErrorState() + override fun commonizationResult() = typeCommonizer.result override fun initialize(first: CirType) { @Suppress("UNCHECKED_CAST") typeCommonizer = when (first) { - is CirClassType -> ClassTypeCommonizer(classifiers) - is CirTypeAliasType -> TypeAliasTypeCommonizer(classifiers) + is CirClassOrTypeAliasType -> ClassOrTypeAliasTypeCommonizer(classifiers) is CirTypeParameterType -> TypeParameterTypeCommonizer() is CirFlexibleType -> FlexibleTypeCommonizer(classifiers) - } as AbstractStandardCommonizer<*, CirType> - - if (first is CirTypeAliasType) { - underlyingTypeCommonizer = TypeCommonizer(classifiers) - } + } as Commonizer<*, CirType> } override fun doCommonizeWith(next: CirType): Boolean { return when (next) { - is CirClassType -> doCommonizeWithClassType(next) - is CirTypeAliasType -> doCommonizeWithTypeAliasType(next) + is CirClassOrTypeAliasType -> (typeCommonizer as? ClassOrTypeAliasTypeCommonizer)?.commonizeWith(next) == true is CirTypeParameterType -> (typeCommonizer as? TypeParameterTypeCommonizer)?.commonizeWith(next) == true is CirFlexibleType -> (typeCommonizer as? FlexibleTypeCommonizer)?.commonizeWith(next) == true } } - - private fun doCommonizeWithClassType(next: CirClassType): Boolean { - if ((typeCommonizer as? ClassTypeCommonizer) == null) { - typeCommonizer = null - } - - val firstAnswer = (typeCommonizer as? ClassTypeCommonizer)?.commonizeWith(next) == true - val secondAnswer = underlyingTypeCommonizer?.commonizeWith(next) == true - return firstAnswer || secondAnswer - } - - private fun doCommonizeWithTypeAliasType(next: CirTypeAliasType): Boolean { - if (typeCommonizer != null && underlyingTypeCommonizer == null) { - underlyingTypeCommonizer = TypeCommonizer(classifiers) - underlyingTypeCommonizer?.commonizeWith(typeCommonizer?.resultOrNull ?: return false) - typeCommonizer = null - } - - val firstAnswer = (typeCommonizer as? TypeAliasTypeCommonizer)?.commonizeWith(next) == true - val secondAnswer = underlyingTypeCommonizer?.commonizeWith(next.underlyingType) == true - return firstAnswer || secondAnswer - } } -private class ClassTypeCommonizer(private val classifiers: CirKnownClassifiers) : AbstractStandardCommonizer() { +internal class ClassTypeCommonizer(private val classifiers: CirKnownClassifiers) : + AbstractStandardCommonizer() { private lateinit var classId: CirEntityId private val outerType = OuterClassTypeCommonizer(classifiers) private lateinit var anyVisibility: Visibility @@ -101,14 +70,15 @@ private class ClassTypeCommonizer(private val classifiers: CirKnownClassifiers) && arguments.commonizeWith(next.arguments) } -private class OuterClassTypeCommonizer(classifiers: CirKnownClassifiers) : +// TODO NOW +internal class OuterClassTypeCommonizer(classifiers: CirKnownClassifiers) : AbstractNullableCommonizer( wrappedCommonizerFactory = { ClassTypeCommonizer(classifiers) }, extractor = { it }, builder = { it } ) -private class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifiers) : +internal class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifiers) : AbstractStandardCommonizer() { private lateinit var typeAliasId: CirEntityId @@ -125,11 +95,11 @@ private class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifie override fun initialize(first: CirTypeAliasType) { typeAliasId = first.classifierId - isMarkedNullable = first.isMarkedNullable + isMarkedNullable = first.expandedType().isMarkedNullable } override fun doCommonizeWith(next: CirTypeAliasType): Boolean { - if (isMarkedNullable != next.isMarkedNullable || typeAliasId != next.classifierId) + if (isMarkedNullable != next.expandedType().isMarkedNullable || typeAliasId != next.classifierId) return false if (commonizedTypeBuilder == null) { @@ -152,7 +122,7 @@ private class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifie } // builds a new type for "common" library fragment for the given combination of type alias types in "platform" fragments - private interface CommonizedTypeAliasTypeBuilder { + internal interface CommonizedTypeAliasTypeBuilder { fun build(typeAliasId: CirEntityId, arguments: List, isMarkedNullable: Boolean): CirClassOrTypeAliasType companion object { @@ -161,7 +131,7 @@ private class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifie override fun build(typeAliasId: CirEntityId, arguments: List, isMarkedNullable: Boolean) = CirClassType.createInterned( classId = typeAliasId, - outerType = null, // there can't be outer type + outerType = null, // there can't be outer type // TODO NOW!!! visibility = commonClass.visibility, arguments = arguments, isMarkedNullable = isMarkedNullable @@ -257,7 +227,7 @@ private fun commonizeTypeAlias(typeAliasId: CirEntityId, classifiers: CirKnownCl return create(typeAliasNode?.commonDeclaration?.invoke() ?: classNode?.commonDeclaration?.invoke()) } -private class CommonizedTypeAliasAnswer(val commonized: Boolean, val commonClassifier: CirClassifier?) { +internal class CommonizedTypeAliasAnswer(val commonized: Boolean, val commonClassifier: CirClassifier?) { companion object { val SUCCESS_FROM_DEPENDENCY_LIBRARY = CommonizedTypeAliasAnswer(true, null) val FAILURE_MISSING_IN_SOME_TARGET = CommonizedTypeAliasAnswer(false, null) diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirTypeAliasNode.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirTypeAliasNode.kt index 68cc581af4f..9218bb1e24c 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirTypeAliasNode.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirTypeAliasNode.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.commonizer.mergedtree -import org.jetbrains.kotlin.commonizer.cir.CirClassifier import org.jetbrains.kotlin.commonizer.cir.CirEntityId import org.jetbrains.kotlin.commonizer.cir.CirTypeAlias import org.jetbrains.kotlin.commonizer.utils.CommonizedGroup @@ -14,8 +13,8 @@ import org.jetbrains.kotlin.storage.NullableLazyValue class CirTypeAliasNode( val id: CirEntityId, override val targetDeclarations: CommonizedGroup, - override val commonDeclaration: NullableLazyValue, -) : CirClassifierNode, CirNodeWithLiftingUp { + override val commonDeclaration: NullableLazyValue, +) : CirClassifierNode, CirNodeWithLiftingUp { override fun accept(visitor: CirNodeVisitor, data: T): R = visitor.visitTypeAliasNode(this, data) diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/nodeBuilders.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/nodeBuilders.kt index 7e60847736c..e3cb3888b86 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/nodeBuilders.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/nodeBuilders.kt @@ -6,9 +6,9 @@ package org.jetbrains.kotlin.commonizer.mergedtree import org.jetbrains.kotlin.commonizer.cir.CirClassRecursionMarker -import org.jetbrains.kotlin.commonizer.cir.CirClassifierRecursionMarker import org.jetbrains.kotlin.commonizer.cir.CirDeclaration import org.jetbrains.kotlin.commonizer.cir.CirEntityId +import org.jetbrains.kotlin.commonizer.cir.CirTypeAliasRecursionMarker import org.jetbrains.kotlin.commonizer.core.* import org.jetbrains.kotlin.commonizer.utils.CommonizedGroup import org.jetbrains.kotlin.storage.NullableLazyValue @@ -116,7 +116,7 @@ internal fun buildTypeAliasNode( size = size, nodeRelationship = null, commonizerProducer = { TypeAliasCommonizer(classifiers) }, - recursionMarker = CirClassifierRecursionMarker, + recursionMarker = CirTypeAliasRecursionMarker, nodeProducer = { targetDeclarations, commonDeclaration -> CirTypeAliasNode(typeAliasId, targetDeclarations, commonDeclaration).also { classifiers.commonizedNodes.addTypeAliasNode(typeAliasId, it) @@ -148,12 +148,8 @@ internal fun commonize( targetDeclarations: CommonizedGroup, commonizer: Commonizer ): R? { - for (targetDeclaration in targetDeclarations) { - if (targetDeclaration == null || !commonizer.commonizeWith(targetDeclaration)) - return null - } - - return commonizer.result + if (targetDeclarations.any { it == null }) return null + return commonizer.commonize(targetDeclarations.filterNotNull()) } @Suppress("NOTHING_TO_INLINE") @@ -176,4 +172,4 @@ private fun CirNodeRelationship?.shouldCommonize(): Boolean { is CirNodeRelationship.PreferredNode -> node.commonDeclaration() == null is CirNodeRelationship.Composite -> relationships.all { it.shouldCommonize() } } -} \ No newline at end of file +} diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/core/TypeCommonizerTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/core/TypeCommonizerTest.kt index 4728ce83681..72c710d636a 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/core/TypeCommonizerTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/core/TypeCommonizerTest.kt @@ -103,21 +103,21 @@ class TypeCommonizerTest : AbstractCommonizerTest() { fun classTypesInUserPackageWithDifferentNames1() = doTestFailure( mockClassType("org/sample/Foo"), mockClassType("org/fictitiousPackageName/Foo"), - shouldFailOnFirstVariant = true + shouldFailOnFirstVariant = false ) @Test(expected = IllegalCommonizerStateException::class) fun classTypesInUserPackageWithDifferentNames2() = doTestFailure( mockClassType("org/sample/Foo"), mockClassType("org/sample/Bar"), - shouldFailOnFirstVariant = true + shouldFailOnFirstVariant = false ) @Test(expected = IllegalCommonizerStateException::class) fun classTypesInUserPackageWithDifferentNames3() = doTestFailure( mockClassType("org/sample/Foo"), mockClassType("kotlin/String"), - shouldFailOnFirstVariant = true + shouldFailOnFirstVariant = false ) @Test @@ -364,19 +364,17 @@ class TypeCommonizerTest : AbstractCommonizerTest() { mockTAType("kotlin/sequences/SequenceBuilder", nullable = false) { mockClassType("kotlin/sequences/SequenceScope") } ) - @Test - // why success: nullability of underlying type does not matter if typealias belongs to one of the standard Kotlin packages - fun taTypesInKotlinPackageWithDifferentNullability3() = doTestSuccess( - expected = mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = false) }, + @Test(expected = IllegalCommonizerStateException::class) + // why failure: Different nullability in type-expansion can't be commonized by now + fun taTypesInKotlinPackageWithDifferentNullability3() = doTestFailure( mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = false) }, mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = false) }, mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = true) } ) - @Test - // why success: nullability of underlying type does not matter if typealias belongs to one of the standard Kotlin packages - fun taTypesInKotlinPackageWithDifferentNullability4() = doTestSuccess( - expected = mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = true) }, + @Test(expected = IllegalCommonizerStateException::class) + // why failure: Different nullability in type-expansion can't be commonized by now + fun taTypesInKotlinPackageWithDifferentNullability4() = doTestFailure( mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = true) }, mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = true) }, mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = false) }