From 8272564ca0b9892e3ed6cfe53cd16f726c7f3848 Mon Sep 17 00:00:00 2001 From: "sebastian.sellmair" Date: Fri, 28 May 2021 18:12:32 +0200 Subject: [PATCH] [Commonizer] Prototype: Commonize TypeAliases with Classes ^KT-45992 Fixed ^KT-46061 Fixed --- .../cir/ArtificialCirDeclaration.kt | 32 ++++ .../commonizer/cir/CirClassConstructor.kt | 8 +- .../kotlin/commonizer/cir/CirDeclaration.kt | 5 + .../kotlin/commonizer/cir/CirFunction.kt | 8 +- .../kotlin/commonizer/cir/CirProperty.kt | 6 + .../commonizer/core/TypeAliasCommonizer.kt | 52 +------ .../kotlin/commonizer/core/TypeCommonizer.kt | 30 ++-- .../kotlin/commonizer/core/typeAliasUtils.kt | 3 +- .../org/jetbrains/kotlin/commonizer/facade.kt | 3 + .../mergedtree/CommonizerCondition.kt | 37 +++++ .../commonizer/mergedtree/nodeBuilders.kt | 34 +++-- .../commonizer/metadata/CirTreeSerializer.kt | 6 +- .../transformer/CirDeclarationCount.kt | 87 +++++++++++ .../transformer/CirNodeTransformer.kt | 43 ++++++ .../InlineTypeAliasCirNodeTransformer.kt | 143 ++++++++++++++++++ .../kotlin/commonizer/tree/mergeCirTree.kt | 11 +- .../commonized/common/package_root.kt | 4 +- .../commonized/common/package_root.kt | 10 +- .../commonized/common/package_root.kt | 2 +- .../commonizer/core/TypeCommonizerTest.kt | 54 +------ ...hicalClassAndTypeAliasCommonizationTest.kt | 137 +++++++++++++++++ .../HierarchicalTypeAliasCommonizationTest.kt | 4 +- .../transformer/RunTransformationTest.kt | 51 +++++++ 23 files changed, 615 insertions(+), 155 deletions(-) create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/ArtificialCirDeclaration.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CommonizerCondition.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/CirDeclarationCount.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/CirNodeTransformer.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/InlineTypeAliasCirNodeTransformer.kt create mode 100644 native/commonizer/tests/org/jetbrains/kotlin/commonizer/transformer/RunTransformationTest.kt diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/ArtificialCirDeclaration.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/ArtificialCirDeclaration.kt new file mode 100644 index 00000000000..3adf789921b --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/ArtificialCirDeclaration.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this delegate code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.commonizer.cir + +/** + * Marker for classifiers that are made up to support commonization. + * E.g. TypeAliases might create artificial classes for the types they are pointing to. + */ +internal interface ArtificialCirDeclaration : CirDeclaration + +internal class ArtificialCirModule(val delegate: CirModule) : CirModule by delegate, ArtificialCirDeclaration + +internal fun CirModule.markedArtificial() = ArtificialCirModule(this) + +internal class ArtificialCirClass(val delegate: CirClass) : CirClass by delegate, ArtificialCirDeclaration + +internal fun CirClass.markedArtificial(): ArtificialCirClass = ArtificialCirClass(this) + +internal class ArtificialCirClassConstructor(val delegate: CirClassConstructor) : CirClassConstructor by delegate, ArtificialCirDeclaration + +internal fun CirClassConstructor.markedArtificial() = ArtificialCirClassConstructor(this) + +internal class ArtificialCirFunction(val delegate: CirFunction) : CirFunction by delegate, ArtificialCirDeclaration + +internal fun CirFunction.markedArtificial() = ArtificialCirFunction(this) + +internal class ArtificialCirProperty(val delegate: CirProperty) : CirProperty by delegate, ArtificialCirDeclaration + +internal fun CirProperty.markedArtificial() = ArtificialCirProperty(this) diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirClassConstructor.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirClassConstructor.kt index ebaadc7a29b..af9e9ec3ef4 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirClassConstructor.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirClassConstructor.kt @@ -18,6 +18,8 @@ interface CirClassConstructor : val isPrimary: Boolean override val containingClass: CirContainingClass // non-nullable + override fun withContainingClass(containingClass: CirContainingClass): CirClassConstructor + companion object { @Suppress("NOTHING_TO_INLINE") inline fun create( @@ -48,4 +50,8 @@ data class CirClassConstructorImpl( override var valueParameters: List, override var hasStableParameterNames: Boolean, override val isPrimary: Boolean -) : CirClassConstructor +) : CirClassConstructor { + override fun withContainingClass(containingClass: CirContainingClass): CirClassConstructor { + return copy(containingClass = containingClass) + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirDeclaration.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirDeclaration.kt index 4acb31e0dc6..943a61740a5 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirDeclaration.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirDeclaration.kt @@ -41,8 +41,13 @@ interface CirHasModality { val modality: Modality } + interface CirMaybeCallableMemberOfClass { val containingClass: CirContainingClass? // null assumes no containing class + + fun withContainingClass(containingClass: CirContainingClass): CirMaybeCallableMemberOfClass = object : CirMaybeCallableMemberOfClass { + override val containingClass: CirContainingClass = containingClass + } } /** diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirFunction.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirFunction.kt index 6fc5354b87f..a1adb05efe8 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirFunction.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirFunction.kt @@ -12,6 +12,8 @@ import org.jetbrains.kotlin.descriptors.Visibility interface CirFunction : CirFunctionOrProperty, CirCallableMemberWithParameters { val modifiers: CirFunctionModifiers + override fun withContainingClass(containingClass: CirContainingClass): CirFunction + companion object { @Suppress("NOTHING_TO_INLINE") inline fun create( @@ -57,4 +59,8 @@ data class CirFunctionImpl( override val returnType: CirType, override val kind: CallableMemberDescriptor.Kind, override val modifiers: CirFunctionModifiers -) : CirFunction +) : CirFunction { + override fun withContainingClass(containingClass: CirContainingClass): CirFunction { + return copy(containingClass = containingClass) + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirProperty.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirProperty.kt index 01a82097f21..ec694c0dff6 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirProperty.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirProperty.kt @@ -21,6 +21,8 @@ interface CirProperty : CirFunctionOrProperty, CirLiftedUpDeclaration { val delegateFieldAnnotations: List val compileTimeInitializer: CirConstantValue + override fun withContainingClass(containingClass: CirContainingClass): CirProperty + companion object { @Suppress("NOTHING_TO_INLINE") inline fun create( @@ -90,4 +92,8 @@ data class CirPropertyImpl( ) : CirProperty { // const property in "common" fragment is already lifted up override val isLiftedUp get() = isConst + + override fun withContainingClass(containingClass: CirContainingClass): CirProperty { + return copy(containingClass = containingClass) + } } 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 632fda762f5..3f854eb80c4 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeAliasCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeAliasCommonizer.kt @@ -16,26 +16,21 @@ 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. - * - * Tertiary (backup) branch: - * - Produce an "expect class" for "common" fragment and the corresponding "actual typealias" declarations for each platform fragment. */ class TypeAliasCommonizer(classifiers: CirKnownClassifiers) : AbstractStandardCommonizer() { private val primary = TypeAliasShortCircuitingCommonizer(classifiers) private val secondary = TypeAliasLiftingUpCommonizer(classifiers) - private val tertiary = TypeAliasExpectClassCommonizer() - override fun commonizationResult(): CirClassifier = primary.resultOrNull ?: secondary.resultOrNull ?: tertiary.result + override fun commonizationResult(): CirClassifier = primary.resultOrNull ?: secondary.result override fun initialize(first: CirTypeAlias) = Unit override fun doCommonizeWith(next: CirTypeAlias): Boolean { val primaryResult = primary.commonizeWith(next) val secondaryResult = secondary.commonizeWith(next) - val tertiaryResult = tertiary.commonizeWith(next) // Note: don't call commonizeWith() functions in return statement to avoid short-circuiting! - return primaryResult || secondaryResult || tertiaryResult + return primaryResult || secondaryResult } } @@ -94,9 +89,6 @@ private class TypeAliasLiftingUpCommonizer(classifiers: CirKnownClassifiers) : A ) } - val resultOrNull: CirTypeAlias? - get() = if (hasResult) commonizationResult() else null - override fun initialize(first: CirTypeAlias) { name = first.name } @@ -106,43 +98,3 @@ private class TypeAliasLiftingUpCommonizer(classifiers: CirKnownClassifiers) : A && underlyingType.commonizeWith(next.underlyingType) && visibility.commonizeWith(next) } - -private class TypeAliasExpectClassCommonizer : AbstractStandardCommonizer() { - private lateinit var name: CirName - private val classVisibility = VisibilityCommonizer.equalizing() - - override fun commonizationResult(): CirClass = CirClass.create( - annotations = emptyList(), - name = name, - typeParameters = emptyList(), - visibility = classVisibility.result, - modality = Modality.FINAL, - kind = ClassKind.CLASS, - companion = null, - isCompanion = false, - isData = false, - isValue = false, - isInner = false, - isExternal = false - ) - - override fun initialize(first: CirTypeAlias) { - name = first.name - } - - override fun doCommonizeWith(next: CirTypeAlias): Boolean { - if (next.typeParameters.isNotEmpty()) - return false // TAs with declared type parameters can't be commonized - - val underlyingType = next.underlyingType as? CirClassType ?: return false // right-hand side could have only class - return hasNoArguments(underlyingType) // TAs with functional types or types with arguments at the right-hand side can't be commonized - && classVisibility.commonizeWith(underlyingType) // the visibilities of the right-hand classes should be equal - } - - private tailrec fun hasNoArguments(type: CirClassType?): Boolean = - when { - type == null -> true - type.arguments.isNotEmpty() -> false - else -> hasNoArguments(type.outerType) - } -} 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 d25bf9a47df..fafa1fa2e52 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeCommonizer.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.commonizer.core import org.jetbrains.kotlin.commonizer.cir.* import org.jetbrains.kotlin.commonizer.core.CommonizedTypeAliasAnswer.Companion.FAILURE_MISSING_IN_SOME_TARGET import org.jetbrains.kotlin.commonizer.core.CommonizedTypeAliasAnswer.Companion.SUCCESS_FROM_DEPENDENCY_LIBRARY +import org.jetbrains.kotlin.commonizer.core.CommonizedTypeAliasAnswer.Companion.create import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers import org.jetbrains.kotlin.commonizer.utils.isUnderKotlinNativeSyntheticPackages import org.jetbrains.kotlin.descriptors.Visibility @@ -203,16 +204,9 @@ private fun commonizeClass(classId: CirEntityId, classifiers: CirKnownClassifier return true } - return when (val node = classifiers.commonizedNodes.classNode(classId)) { - null -> { - // No node means that the type alias was not subject for commonization. It is missing in some target(s) => not commonized. - false - } - else -> { - // Common declaration in node is not null -> successfully commonized. - (node.commonDeclaration() != null) - } - } + // Looking for a a node that provides a non-null (successfully commonized) classifier declaration + return (classifiers.commonizedNodes.classNode(classId)?.commonDeclaration?.invoke() + ?: classifiers.commonizedNodes.typeAliasNode(classId)?.commonDeclaration?.invoke()) != null } private fun commonizeTypeAlias(typeAliasId: CirEntityId, classifiers: CirKnownClassifiers): CommonizedTypeAliasAnswer { @@ -221,16 +215,14 @@ private fun commonizeTypeAlias(typeAliasId: CirEntityId, classifiers: CirKnownCl return SUCCESS_FROM_DEPENDENCY_LIBRARY } - return when (val node = classifiers.commonizedNodes.typeAliasNode(typeAliasId)) { - null -> { - // No node means that the type alias was not subject for commonization. It is missing in some target(s) => not commonized. - FAILURE_MISSING_IN_SOME_TARGET - } - else -> { - // Common declaration in node is not null -> successfully commonized. - CommonizedTypeAliasAnswer.create(node.commonDeclaration()) - } + val typeAliasNode = classifiers.commonizedNodes.typeAliasNode(typeAliasId) + val classNode = classifiers.commonizedNodes.classNode(typeAliasId) + + if (typeAliasNode == null && classNode == null) { + return FAILURE_MISSING_IN_SOME_TARGET } + + return create(typeAliasNode?.commonDeclaration?.invoke() ?: classNode?.commonDeclaration?.invoke()) } private class CommonizedTypeAliasAnswer(val commonized: Boolean, val commonClassifier: CirClassifier?) { diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/typeAliasUtils.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/typeAliasUtils.kt index 71d4789c60a..b410f2b6bac 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/typeAliasUtils.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/typeAliasUtils.kt @@ -18,8 +18,7 @@ internal tailrec fun computeSuitableUnderlyingType( return when (underlyingType) { is CirClassType -> underlyingType.withCommonizedArguments(classifiers) is CirTypeAliasType -> if (classifiers.commonDependencies.hasClassifier(underlyingType.classifierId)) - underlyingType.withCommonizedArguments(classifiers) - else + underlyingType.withCommonizedArguments(classifiers) else computeSuitableUnderlyingType(classifiers, underlyingType.underlyingType) } } diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/facade.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/facade.kt index 60e7010a9d5..c4d6b6ed635 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/facade.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/facade.kt @@ -16,6 +16,8 @@ import org.jetbrains.kotlin.commonizer.mergedtree.CirNode.Companion.indexOfCommo import org.jetbrains.kotlin.commonizer.mergedtree.CirNode.Companion.targetIndices import org.jetbrains.kotlin.commonizer.mergedtree.CirRootNode import org.jetbrains.kotlin.commonizer.metadata.CirTreeSerializer.serializeSingleTarget +import org.jetbrains.kotlin.commonizer.transformer.Checked.Companion.invoke +import org.jetbrains.kotlin.commonizer.transformer.InlineTypeAliasCirNodeTransformer import org.jetbrains.kotlin.commonizer.tree.CirTreeRoot import org.jetbrains.kotlin.commonizer.tree.assembleCirTree import org.jetbrains.kotlin.commonizer.tree.deserializeCirTree @@ -50,6 +52,7 @@ private fun commonize( ) val mergedTree = merge(storageManager, classifiers, cirTrees) ?: return null + InlineTypeAliasCirNodeTransformer(storageManager, classifiers).invoke(mergedTree) mergedTree.accept(CommonizationVisitor(classifiers, mergedTree), Unit) parameters.logProgress("Commonized declarations for $target") diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CommonizerCondition.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CommonizerCondition.kt new file mode 100644 index 00000000000..36a4dd64ed7 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CommonizerCondition.kt @@ -0,0 +1,37 @@ +/* + * 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.mergedtree + +import org.jetbrains.kotlin.commonizer.cir.CirDeclaration +import org.jetbrains.kotlin.storage.NullableLazyValue + + +internal fun interface CommonizerCondition { + + fun allowCommonization(): Boolean + + infix fun and(other: CommonizerCondition) = CommonizerCondition { allowCommonization() && other.allowCommonization() } + + companion object Factory { + fun none(): CommonizerCondition = NoneCommonizerCondition + + fun nodeIsNotCommonized(node: CirNode<*, *>) = CommonizerCondition { node.commonDeclaration() == null } + + fun parent(parent: CirNode<*, *>): ParentNodeCommonizerCondition = ParentNodeCommonizerCondition(parent.commonDeclaration) + + fun parent(parent: CirNode<*, *>?): CommonizerCondition = if (parent == null) none() else parent(parent) + } +} + +private object NoneCommonizerCondition : CommonizerCondition { + override fun allowCommonization(): Boolean = true +} + +internal class ParentNodeCommonizerCondition( + private val parentNodeCommonDeclaration: NullableLazyValue +) : CommonizerCondition { + override fun allowCommonization(): Boolean = parentNodeCommonDeclaration() != null +} 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 b8e2351049d..330e5719e1f 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/nodeBuilders.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/nodeBuilders.kt @@ -20,6 +20,7 @@ internal fun buildRootNode( ): CirRootNode = buildNode( storageManager = storageManager, size = size, + commonizerCondition = CommonizerCondition.none(), commonizerProducer = ::RootCommonizer, nodeProducer = ::CirRootNode ) @@ -30,6 +31,7 @@ internal fun buildModuleNode( ): CirModuleNode = buildNode( storageManager = storageManager, size = size, + commonizerCondition = CommonizerCondition.none(), commonizerProducer = ::ModuleCommonizer, nodeProducer = ::CirModuleNode ) @@ -40,6 +42,7 @@ internal fun buildPackageNode( ): CirPackageNode = buildNode( storageManager = storageManager, size = size, + commonizerCondition = CommonizerCondition.none(), commonizerProducer = ::PackageCommonizer, nodeProducer = ::CirPackageNode ) @@ -48,11 +51,11 @@ internal fun buildPropertyNode( storageManager: StorageManager, size: Int, classifiers: CirKnownClassifiers, - parentCommonDeclaration: NullableLazyValue<*>? + condition: CommonizerCondition = CommonizerCondition.none(), ): CirPropertyNode = buildNode( storageManager = storageManager, size = size, - parentCommonDeclaration = parentCommonDeclaration, + commonizerCondition = condition, commonizerProducer = { PropertyCommonizer(classifiers) }, nodeProducer = ::CirPropertyNode ) @@ -61,11 +64,11 @@ internal fun buildFunctionNode( storageManager: StorageManager, size: Int, classifiers: CirKnownClassifiers, - parentCommonDeclaration: NullableLazyValue<*>? + condition: CommonizerCondition = CommonizerCondition.none(), ): CirFunctionNode = buildNode( storageManager = storageManager, size = size, - parentCommonDeclaration = parentCommonDeclaration, + commonizerCondition = condition, commonizerProducer = { FunctionCommonizer(classifiers) }, nodeProducer = ::CirFunctionNode ) @@ -74,12 +77,12 @@ internal fun buildClassNode( storageManager: StorageManager, size: Int, classifiers: CirKnownClassifiers, - parentCommonDeclaration: NullableLazyValue<*>?, + condition: CommonizerCondition = CommonizerCondition.none(), classId: CirEntityId ): CirClassNode = buildNode( storageManager = storageManager, size = size, - parentCommonDeclaration = parentCommonDeclaration, + commonizerCondition = condition, commonizerProducer = { ClassCommonizer(classifiers) }, recursionMarker = CirClassRecursionMarker, nodeProducer = { targetDeclarations, commonDeclaration -> @@ -89,15 +92,16 @@ internal fun buildClassNode( } ) + internal fun buildClassConstructorNode( storageManager: StorageManager, size: Int, classifiers: CirKnownClassifiers, - parentCommonDeclaration: NullableLazyValue<*>? + condition: ParentNodeCommonizerCondition, ): CirClassConstructorNode = buildNode( storageManager = storageManager, size = size, - parentCommonDeclaration = parentCommonDeclaration, + commonizerCondition = condition, commonizerProducer = { ClassConstructorCommonizer(classifiers) }, nodeProducer = ::CirClassConstructorNode ) @@ -110,6 +114,7 @@ internal fun buildTypeAliasNode( ): CirTypeAliasNode = buildNode( storageManager = storageManager, size = size, + commonizerCondition = CommonizerCondition.none(), commonizerProducer = { TypeAliasCommonizer(classifiers) }, recursionMarker = CirClassifierRecursionMarker, nodeProducer = { targetDeclarations, commonDeclaration -> @@ -122,14 +127,14 @@ internal fun buildTypeAliasNode( private fun > buildNode( storageManager: StorageManager, size: Int, - parentCommonDeclaration: NullableLazyValue<*>? = null, + commonizerCondition: CommonizerCondition, commonizerProducer: () -> Commonizer, recursionMarker: R? = null, nodeProducer: (CommonizedGroup, NullableLazyValue) -> N ): N { val targetDeclarations = CommonizedGroup(size) - val commonComputable = { commonize(parentCommonDeclaration, targetDeclarations, commonizerProducer()) } + val commonComputable = { commonize(commonizerCondition, targetDeclarations, commonizerProducer()) } val commonLazyValue = if (recursionMarker != null) storageManager.createRecursionTolerantNullableLazyValue(commonComputable, recursionMarker) @@ -153,14 +158,13 @@ internal fun commonize( @Suppress("NOTHING_TO_INLINE") private inline fun commonize( - parentCommonDeclaration: NullableLazyValue<*>?, + commonizerCondition: CommonizerCondition, targetDeclarations: CommonizedGroup, commonizer: Commonizer ): R? { - if (parentCommonDeclaration != null && parentCommonDeclaration.invoke() == null) { - // don't commonize declaration if it's parent failed to commonize - return null + if (commonizerCondition.allowCommonization()) { + return commonize(targetDeclarations, commonizer) } - return commonize(targetDeclarations, commonizer) + return null } diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/metadata/CirTreeSerializer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/metadata/CirTreeSerializer.kt index 9f2cecc2642..56f7c234274 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/metadata/CirTreeSerializer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/metadata/CirTreeSerializer.kt @@ -143,6 +143,7 @@ private class CirTreeSerializationVisitor( classContext: CirTreeSerializationContext ): KmClass? { val cirClass = classContext.get(node) ?: return null + val classTypeParametersCount = cirClass.typeParameters.size val fullClassName = classContext.currentPath.toString() @@ -403,7 +404,8 @@ internal data class CirTreeSerializationContext( } inline fun get(node: CirNode<*, *>): T? { - return (if (isCommon) node.commonDeclaration() else node.targetDeclarations[targetIndex]) as T? + return ((if (isCommon) node.commonDeclaration() else node.targetDeclarations[targetIndex]) as T?) + .takeIf { it !is ArtificialCirDeclaration } } inline fun get(node: CirNodeWithLiftingUp<*, *>): T? { @@ -411,7 +413,7 @@ internal data class CirTreeSerializationContext( isCommon -> node.commonDeclaration() as T? node.isLiftedUp -> null else -> node.targetDeclarations[targetIndex] as T? - } + }.takeIf { it !is ArtificialCirDeclaration } } companion object { diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/CirDeclarationCount.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/CirDeclarationCount.kt new file mode 100644 index 00000000000..5bd16892baa --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/CirDeclarationCount.kt @@ -0,0 +1,87 @@ +/* + * 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. + */ + +/* + * 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.transformer + +import org.jetbrains.kotlin.commonizer.cir.ArtificialCirDeclaration +import org.jetbrains.kotlin.commonizer.mergedtree.* + +internal data class CirDeclarationCount( + val artificialNodeCount: Int, + val nonArtificialNodeCount: Int +) + +internal data class MutableCirDeclarationCount( + var artificialNodeCount: Int = 0, + var nonArtificialNodeCount: Int = 0 +) { + fun toCirDeclarationCount() = CirDeclarationCount( + artificialNodeCount = artificialNodeCount, + nonArtificialNodeCount = nonArtificialNodeCount + ) +} + +internal fun CirNode<*, *>.countCirDeclarations(): CirDeclarationCount { + val counter = MutableCirDeclarationCount() + accept(CirNodeCounterVisitor, counter) + return counter.toCirDeclarationCount() +} + +private object CirNodeCounterVisitor : CirNodeVisitor { + + private operator fun MutableCirDeclarationCount.plusAssign(node: CirNode<*, *>) { + node.targetDeclarations.forEach { declaration -> + if (declaration == null) return@forEach + if (declaration is ArtificialCirDeclaration) artificialNodeCount++ else nonArtificialNodeCount++ + } + } + + override fun visitRootNode(node: CirRootNode, data: MutableCirDeclarationCount) { + data += node + node.modules.forEach { (_, module) -> module.accept(this, data) } + } + + override fun visitModuleNode(node: CirModuleNode, data: MutableCirDeclarationCount) { + data += node + node.packages.forEach { (_, pkg) -> pkg.accept(this, data) } + } + + override fun visitPackageNode(node: CirPackageNode, data: MutableCirDeclarationCount) { + data += node + node.typeAliases.forEach { (_, typeAlias) -> typeAlias.accept(this, data) } + node.properties.forEach { (_, property) -> property.accept(this, data) } + node.functions.forEach { (_, function) -> function.accept(this, data) } + node.classes.forEach { (_, clazz) -> clazz.accept(this, data) } + } + + override fun visitPropertyNode(node: CirPropertyNode, data: MutableCirDeclarationCount) { + data += node + } + + override fun visitFunctionNode(node: CirFunctionNode, data: MutableCirDeclarationCount) { + data += node + } + + override fun visitClassNode(node: CirClassNode, data: MutableCirDeclarationCount) { + data += node + node.properties.forEach { (_, property) -> property.accept(this, data) } + node.functions.forEach { (_, function) -> function.accept(this, data) } + node.constructors.forEach { (_, constructor) -> constructor.accept(this, data) } + node.classes.forEach { (_, clazz) -> clazz.accept(this, data) } + } + + override fun visitClassConstructorNode(node: CirClassConstructorNode, data: MutableCirDeclarationCount) { + data += node + } + + override fun visitTypeAliasNode(node: CirTypeAliasNode, data: MutableCirDeclarationCount) { + data += node + } +} \ No newline at end of file diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/CirNodeTransformer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/CirNodeTransformer.kt new file mode 100644 index 00000000000..f2eed83b01b --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/CirNodeTransformer.kt @@ -0,0 +1,43 @@ +/* + * 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.transformer + +import org.jetbrains.kotlin.commonizer.mergedtree.CirRootNode + +internal fun interface CirNodeTransformer { + + sealed class Context { + object Unchecked : Context() + } + + operator fun Context.invoke(root: CirRootNode) +} + +internal class Checked private constructor() : CirNodeTransformer.Context() { + companion object { + private val areAssertionsEnabled = this::class.java.desiredAssertionStatus() + + operator fun CirNodeTransformer.invoke(cirRootNode: CirRootNode) { + val context = if (areAssertionsEnabled) Checked() else Unchecked + val declarationCountBefore = if (areAssertionsEnabled) cirRootNode.countCirDeclarations() else null + + with(this) { + context.invoke(cirRootNode) + } + + if (declarationCountBefore != null) { + val declarationCountAfter = cirRootNode.countCirDeclarations() + if (declarationCountAfter.nonArtificialNodeCount > declarationCountBefore.nonArtificialNodeCount) { + throw AssertionError( + "$this attached declarations not marked as artificial\n" + + "Declaration Count before: $declarationCountBefore\n" + + "Declaration Count after: $declarationCountAfter" + ) + } + } + } + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/InlineTypeAliasCirNodeTransformer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/InlineTypeAliasCirNodeTransformer.kt new file mode 100644 index 00000000000..e9d3ce69ee1 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/InlineTypeAliasCirNodeTransformer.kt @@ -0,0 +1,143 @@ +/* + * 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.transformer + +import org.jetbrains.kotlin.commonizer.cir.* +import org.jetbrains.kotlin.commonizer.mergedtree.* +import org.jetbrains.kotlin.commonizer.transformer.CirNodeTransformer.Context +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.storage.StorageManager + +internal class InlineTypeAliasCirNodeTransformer( + private val storageManager: StorageManager, + private val classifiers: CirKnownClassifiers +) : CirNodeTransformer { + override fun Context.invoke(root: CirRootNode) { + root.modules.values.forEach(::invoke) + } + + private operator fun invoke(module: CirModuleNode) { + val classNodeIndex = ClassNodeIndex(module) + + module.packages.values.forEach { packageNode -> + packageNode.typeAliases.values.forEach { typeAliasNode -> + val targetClassNode = classNodeIndex[typeAliasNode.id] ?: packageNode.createArtificialClassNode(typeAliasNode) + inlineTypeAliasIfPossible(classNodeIndex, typeAliasNode, targetClassNode) + } + } + } + + private fun inlineTypeAliasIfPossible(classes: ClassNodeIndex, fromTypeAliasNode: CirTypeAliasNode, intoClassNode: CirClassNode) { + fromTypeAliasNode.targetDeclarations.forEachIndexed { targetIndex, typeAlias -> + if (typeAlias != null) { + inlineTypeAliasIfPossible(classes, typeAlias, intoClassNode, targetIndex) + } + } + } + + private fun inlineTypeAliasIfPossible( + classes: ClassNodeIndex, fromTypeAlias: CirTypeAlias, intoClassNode: CirClassNode, targetIndex: Int + ) { + if (fromTypeAlias.typeParameters.isNotEmpty()) { + // Inlining parameterized TAs is not supported yet + return + } + + if (fromTypeAlias.underlyingType.arguments.isNotEmpty() || + fromTypeAlias.underlyingType.run { this as? CirClassType }?.outerType?.arguments?.isNotEmpty() == true + ) { + // Inlining TAs with parameterized underlying types is not supported yet + return + } + + if (intoClassNode.targetDeclarations[targetIndex] != null) { + // No empty spot to inline the type-alias into + return + } + + val fromAliasedClassNode = classes[fromTypeAlias.expandedType.classifierId] + + val intoArtificialClass = ArtificialAliasedCirClass( + pointingTypeAlias = fromTypeAlias, + pointedClass = fromAliasedClassNode?.targetDeclarations?.get(targetIndex) ?: fromTypeAlias.toArtificialCirClass() + ) + + intoClassNode.targetDeclarations[targetIndex] = intoArtificialClass + + if (fromAliasedClassNode != null && !fromTypeAlias.expandedType.isMarkedNullable) { + inlineArtificialMembers(fromAliasedClassNode, intoClassNode, intoArtificialClass, targetIndex) + } + } + + private fun inlineArtificialMembers( + fromAliasedClassNode: CirClassNode, + intoClassNode: CirClassNode, + intoClass: CirClass, + targetIndex: Int + ) { + val targetSize = intoClassNode.targetDeclarations.size + + fromAliasedClassNode.constructors.forEach { (key, aliasedConstructorNode) -> + val aliasedConstructor = aliasedConstructorNode.targetDeclarations[targetIndex] ?: return@forEach + intoClassNode.constructors.getOrPut(key) { + buildClassConstructorNode(storageManager, targetSize, classifiers, CommonizerCondition.parent(intoClassNode)) + }.targetDeclarations[targetIndex] = aliasedConstructor.withContainingClass(intoClass).markedArtificial() + } + + fromAliasedClassNode.functions.forEach { (key, aliasedFunctionNode) -> + val aliasedFunction = aliasedFunctionNode.targetDeclarations[targetIndex] ?: return@forEach + intoClassNode.functions.getOrPut(key) { + buildFunctionNode(storageManager, targetSize, classifiers, CommonizerCondition.parent(intoClassNode)) + }.targetDeclarations[targetIndex] = aliasedFunction.withContainingClass(intoClass).markedArtificial() + } + + fromAliasedClassNode.properties.forEach { (key, aliasedPropertyNode) -> + val aliasedProperty = aliasedPropertyNode.targetDeclarations[targetIndex] ?: return@forEach + intoClassNode.properties.getOrPut(key) { + buildPropertyNode(storageManager, targetSize, classifiers, CommonizerCondition.parent(intoClassNode)) + }.targetDeclarations[targetIndex] = aliasedProperty.withContainingClass(intoClass).markedArtificial() + } + } + + private fun CirPackageNode.createArtificialClassNode(typeAliasNode: CirTypeAliasNode): CirClassNode { + val classNode = buildClassNode( + storageManager = storageManager, + size = typeAliasNode.targetDeclarations.size, + classifiers = classifiers, + // This artificial class node should only try to commonize if the package node is commonized + // and if the original typeAliasNode cannot be commonized. + // Therefore, this artificial class node acts as a fallback with the original type-alias being still the preferred + // option for commonization + condition = CommonizerCondition.parent(this) and CommonizerCondition.nodeIsNotCommonized(typeAliasNode), + classId = typeAliasNode.id + ) + this.classes[typeAliasNode.classifierName] = classNode + return classNode + } +} + +private typealias ClassNodeIndex = Map + +private fun ClassNodeIndex(module: CirModuleNode): ClassNodeIndex = module.packages.values + .flatMap { pkg -> pkg.classes.values } + .associateBy { clazz -> clazz.id } + +private data class ArtificialAliasedCirClass( + val pointingTypeAlias: CirTypeAlias, + val pointedClass: CirClass +) : CirClass by pointedClass, ArtificialCirDeclaration { + override val name: CirName = pointingTypeAlias.name + override var companion: CirName? + get() = null + set(_) = throw UnsupportedOperationException("Can't set companion on artificial class (pointed by $pointingTypeAlias)") +} + +private fun CirTypeAlias.toArtificialCirClass(): CirClass = CirClass.create( + annotations = emptyList(), name = name, typeParameters = typeParameters, + visibility = this.visibility, modality = Modality.FINAL, kind = ClassKind.CLASS, + companion = null, isCompanion = false, isData = false, isValue = false, isInner = false, isExternal = false +).also { it.supertypes = emptyList() }.markedArtificial() diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/tree/mergeCirTree.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/tree/mergeCirTree.kt index e8298a0452c..535929f924b 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/tree/mergeCirTree.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/tree/mergeCirTree.kt @@ -52,7 +52,7 @@ internal fun CirNodeWithMembers<*, *>.buildClass( context: TargetBuildingContext, treeClass: CirTreeClass, parent: CirNode<*, *>? = null ) { val classNode = classes.getOrPut(treeClass.clazz.name) { - buildClassNode(context.storageManager, context.targets, context.classifiers, parent?.commonDeclaration, treeClass.id) + buildClassNode(context.storageManager, context.targets, context.classifiers, CommonizerCondition.parent(parent), treeClass.id) } classNode.targetDeclarations[context.targetIndex] = treeClass.clazz treeClass.functions.forEach { function -> classNode.buildFunction(context, function, classNode) } @@ -65,7 +65,7 @@ internal fun CirNodeWithMembers<*, *>.buildFunction( context: TargetBuildingContext, treeFunction: CirTreeFunction, parent: CirNode<*, *>? = null ) { val functionNode = functions.getOrPut(treeFunction.approximationKey) { - buildFunctionNode(context.storageManager, context.targets, context.classifiers, parent?.commonDeclaration) + buildFunctionNode(context.storageManager, context.targets, context.classifiers, CommonizerCondition.parent(parent)) } functionNode.targetDeclarations[context.targetIndex] = treeFunction.function } @@ -74,16 +74,16 @@ internal fun CirNodeWithMembers<*, *>.buildProperty( context: TargetBuildingContext, treeProperty: CirTreeProperty, parent: CirNode<*, *>? = null ) { val propertyNode = properties.getOrPut(treeProperty.approximationKey) { - buildPropertyNode(context.storageManager, context.targets, context.classifiers, parent?.commonDeclaration) + buildPropertyNode(context.storageManager, context.targets, context.classifiers, CommonizerCondition.parent(parent)) } propertyNode.targetDeclarations[context.targetIndex] = treeProperty.property } internal fun CirClassNode.buildConstructor( - context: TargetBuildingContext, treeConstructor: CirTreeClassConstructor, parent: CirNode<*, *>? + context: TargetBuildingContext, treeConstructor: CirTreeClassConstructor, parent: CirNode<*, *> ) { val constructorNode = constructors.getOrPut(treeConstructor.approximationKey) { - buildClassConstructorNode(context.storageManager, context.targets, context.classifiers, parent?.commonDeclaration) + buildClassConstructorNode(context.storageManager, context.targets, context.classifiers, CommonizerCondition.parent(parent)) } constructorNode.targetDeclarations[context.targetIndex] = treeConstructor.constructor } @@ -94,4 +94,3 @@ internal fun CirPackageNode.buildTypeAlias(context: TargetBuildingContext, treeT } typeAliasNode.targetDeclarations[context.targetIndex] = treeTypeAlias.typeAlias } - diff --git a/native/commonizer/testData/classifierCommonization/typeAliases/commonized/common/package_root.kt b/native/commonizer/testData/classifierCommonization/typeAliases/commonized/common/package_root.kt index 8ebbde00256..8e1b815a5c5 100644 --- a/native/commonizer/testData/classifierCommonization/typeAliases/commonized/common/package_root.kt +++ b/native/commonizer/testData/classifierCommonization/typeAliases/commonized/common/package_root.kt @@ -1,5 +1,4 @@ expect class A() - // Lifted up type aliases: typealias B = A // class at the RHS typealias C = A // TA at the RHS, expanded to the same class @@ -40,10 +39,11 @@ typealias U = A // same nullability of the RHS class expect class V // different nullability of the RHS class typealias W = A // same nullability of the RHS TA +expect class X // different nullability of the RHS TA typealias Y = V // TA at the RHS with the different nullability of own RHS // Supertypes: -expect class FILE : kotlinx.cinterop.CStructVar +expect class FILE expect constructor(): kotlinx.cinterop.CStructVar typealias uuid_t = kotlinx.cinterop.CArrayPointer typealias __darwin_uuid_t = kotlinx.cinterop.CArrayPointer diff --git a/native/commonizer/testData/classifierCommonization/visibility/commonized/common/package_root.kt b/native/commonizer/testData/classifierCommonization/visibility/commonized/common/package_root.kt index 5955310932d..52a90e90f3b 100644 --- a/native/commonizer/testData/classifierCommonization/visibility/commonized/common/package_root.kt +++ b/native/commonizer/testData/classifierCommonization/visibility/commonized/common/package_root.kt @@ -6,18 +6,18 @@ public typealias E1 = A1 protected typealias E2 = A1 internal typealias E3 = A1 -public expect class E4 +public expect class E4() protected typealias F1 = A1 -public expect class F2 -public expect class F3 +public expect class F2() +public expect class F3() internal typealias G1 = A1 -public expect class G2 +public expect class G2() -public expect class H1 +public expect class H1() public typealias I1 = A1 public typealias I2 = B1 diff --git a/native/commonizer/testData/generalCommonization/annotations/commonized/common/package_root.kt b/native/commonizer/testData/generalCommonization/annotations/commonized/common/package_root.kt index 96a2cea6137..5602670ecda 100644 --- a/native/commonizer/testData/generalCommonization/annotations/commonized/common/package_root.kt +++ b/native/commonizer/testData/generalCommonization/annotations/commonized/common/package_root.kt @@ -19,4 +19,4 @@ expect class AnnotatedClass(value: String) { } typealias AnnotatedLiftedUpTypeAlias = AnnotatedClass -expect class AnnotatedNonLiftedUpTypeAlias +expect class AnnotatedNonLiftedUpTypeAlias expect constructor(expect val value: String) 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 e5f5e6d969b..8d5a79b9da2 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/core/TypeCommonizerTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/core/TypeCommonizerTest.kt @@ -272,14 +272,6 @@ class TypeCommonizerTest : AbstractCommonizerTest() { shouldFailOnFirstVariant = true ) - @Test - // why success: expect class/actual TAs - fun taTypesInUserPackageWithDifferentClasses() = doTestSuccess( - expected = mockClassType("org/sample/FooAlias"), - mockTAType("org/sample/FooAlias") { mockClassType("org/sample/Foo") }, - mockTAType("org/sample/FooAlias") { mockClassType("org/sample/Bar") } - ) - @Test // why success: short-circuiting & lifting up fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass1() = doTestSuccess( @@ -338,28 +330,6 @@ class TypeCommonizerTest : AbstractCommonizerTest() { } ) - @Test - // why success: lifting up outer TA and expect class for inner TA - fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass4() = doTestSuccess( - expected = mockTAType("org/sample/FooAlias") { - mockTAType("org/sample/FooAliasL2") { - mockClassType("org/sample/Foo") - } - }, - - mockTAType("org/sample/FooAlias") { - mockTAType("org/sample/FooAliasL2") { - mockClassType("org/sample/Bar") - } - }, - - mockTAType("org/sample/FooAlias") { - mockTAType("org/sample/FooAliasL2") { - mockClassType("org/sample/Baz") - } - } - ) - @Test // why success: types with the same nullability are treated as equal fun taTypesInKotlinPackageWithSameNullability1() = doTestSuccess( @@ -432,30 +402,16 @@ class TypeCommonizerTest : AbstractCommonizerTest() { fun taTypesInUserPackageWithDifferentNullability1() = doTestFailure( mockTAType("org/sample/FooAlias", nullable = false) { mockClassType("org/sample/Foo") }, mockTAType("org/sample/FooAlias", nullable = false) { mockClassType("org/sample/Foo") }, - mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") } + mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") }, + shouldFailOnFirstVariant = true ) @Test(expected = IllegalCommonizerStateException::class) fun taTypesInUserPackageWithDifferentNullability2() = doTestFailure( mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") }, mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") }, - mockTAType("org/sample/FooAlias", nullable = false) { mockClassType("org/sample/Foo") } - ) - - @Test - // why success: nullability of underlying type does not matter if expect class/actual TAs created - fun taTypesInUserPackageWithDifferentNullability3() = doTestSuccess( - expected = mockClassType("org/sample/FooAlias"), - mockTAType("org/sample/FooAlias") { mockClassType("org/sample/Foo", nullable = false) }, - mockTAType("org/sample/FooAlias") { mockClassType("org/sample/Foo", nullable = true) } - ) - - @Test - // why success: nullability of underlying type does not matter if expect class/actual TAs created - fun taTypesInUserPackageWithDifferentNullability4() = doTestSuccess( - expected = mockClassType("org/sample/FooAlias"), - mockTAType("org/sample/FooAlias") { mockClassType("org/sample/Foo", nullable = true) }, - mockTAType("org/sample/FooAlias") { mockClassType("org/sample/Foo", nullable = false) } + mockTAType("org/sample/FooAlias", nullable = false) { mockClassType("org/sample/Foo") }, + shouldFailOnFirstVariant = true ) private fun prepareCache(variants: Array) { @@ -469,7 +425,7 @@ class TypeCommonizerTest : AbstractCommonizerTest() { storageManager = LockBasedStorageManager.NO_LOCKS, size = variants.size, classifiers = classifiers, - parentCommonDeclaration = null, + condition = CommonizerCondition.none(), classId = type.classifierId ) } diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalClassAndTypeAliasCommonizationTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalClassAndTypeAliasCommonizationTest.kt index 14a09df10f9..9a08fd6a14a 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalClassAndTypeAliasCommonizationTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalClassAndTypeAliasCommonizationTest.kt @@ -104,4 +104,141 @@ class HierarchicalClassAndTypeAliasCommonizationTest : AbstractInlineSourcesComm """.trimIndent() ) } + + fun `test follow typeAlias on both platforms`() { + val result = commonize { + outputTarget("(a, b)") + simpleSingleSourceTarget( + "a", """ + class A { + val x: Int = 42 + } + typealias X = A + """.trimIndent() + ) + + simpleSingleSourceTarget( + "b", """ + class B { + val x: Int = 42 + } + typealias X = B + """.trimIndent() + ) + } + + result.assertCommonized( + "(a, b)", """ + expect class X expect constructor() { + expect val x: Int + } + """.trimIndent() + ) + } + + fun `test follow exact same typeAlias on both platforms`() { + val result = commonize { + outputTarget("(a, b)") + simpleSingleSourceTarget( + "a", """ + class AB { + val x: Int = 42 + } + typealias X = AB + """.trimIndent() + ) + + simpleSingleSourceTarget( + "b", """ + class AB { + val x: Int = 42 + } + typealias X = AB + """.trimIndent() + ) + } + + result.assertCommonized( + "(a, b)", """ + expect class AB expect constructor() { + expect val x: Int + } + typealias X = AB + """.trimIndent() + ) + } + + fun `test aliased class with companion`() { + val result = commonize { + outputTarget("(a, b)") + simpleSingleSourceTarget( + "a", """ + class A { + companion object + } + + typealias X = A + """.trimIndent() + ) + simpleSingleSourceTarget( + "b", """ + class X { + companion object + } + """.trimIndent() + ) + } + + result.assertCommonized("(a, b)", """expect class X expect constructor()""") + } + + fun `test typeAlias with nullability`() { + val result = commonize { + outputTarget("(a, b)") + simpleSingleSourceTarget( + "a", """ + class A + typealias X = A? + """.trimIndent() + ) + + simpleSingleSourceTarget( + "b", """ + class B + typealias X = B + """.trimIndent() + ) + } + + result.assertCommonized("(a, b)", """expect class X""") + } + + fun `test typeAlias chain with nullability`() { + val result = commonize { + outputTarget("(a, b)") + simpleSingleSourceTarget( + "a", """ + class AB + typealias V = AB? + typealias Y = V + """.trimIndent() + ) + + simpleSingleSourceTarget( + "b", """ + class AB + typealias V = AB + typealias Y = V + """.trimIndent() + ) + } + + result.assertCommonized( + "(a, b)", """ + expect class AB expect constructor() + expect class V + typealias Y = V + """.trimIndent() + ) + } } diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalTypeAliasCommonizationTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalTypeAliasCommonizationTest.kt index 2225ef9ed0e..54277f517b5 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalTypeAliasCommonizationTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalTypeAliasCommonizationTest.kt @@ -111,7 +111,7 @@ class HierarchicalTypeAliasCommonizationTest : AbstractInlineSourcesCommonizatio "(e,f)", """expect class x expect constructor()""" ) - result.assertCommonized("((a,b), (c,d))", """expect class x""") - result.assertCommonized("(((a,b), (c,d)), (e,f))", """expect class x""") + result.assertCommonized("((a,b), (c,d))", """expect class x expect constructor()""") + result.assertCommonized("(((a,b), (c,d)), (e,f))", """expect class x expect constructor()""") } } diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/transformer/RunTransformationTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/transformer/RunTransformationTest.kt new file mode 100644 index 00000000000..27a882a0656 --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/transformer/RunTransformationTest.kt @@ -0,0 +1,51 @@ +/* + * 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.transformer + +import org.jetbrains.kotlin.commonizer.cir.CirModule +import org.jetbrains.kotlin.commonizer.cir.CirName +import org.jetbrains.kotlin.commonizer.cir.markedArtificial +import org.jetbrains.kotlin.commonizer.mergedtree.buildModuleNode +import org.jetbrains.kotlin.commonizer.mergedtree.buildRootNode +import org.jetbrains.kotlin.commonizer.transformer.Checked.Companion.invoke +import org.jetbrains.kotlin.storage.LockBasedStorageManager +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertIs + +class RunTransformationTest { + + private val storageManager = LockBasedStorageManager("test") + + @Test + fun `fails when attaching non-artificial declarations`() { + val badTransformer = CirNodeTransformer { root -> + root.modules[CirName.create("bad-module")] = buildModuleNode(storageManager, 1).also { moduleNode -> + moduleNode.targetDeclarations[0] = CirModule.create(CirName.create("bad-module")) + } + } + + val node = buildRootNode(storageManager, 0) + assertFailsWith { badTransformer(node) } + } + + @Test + fun `when attaching non-artificial declarations`() { + val goodTransformer = CirNodeTransformer { root -> + assertIs(this, "Expected 'Checked' context") + + root.modules[CirName.create("artificial-module")] = buildModuleNode(storageManager, 1).also { moduleNode -> + moduleNode.targetDeclarations[0] = CirModule.create(CirName.create("artificial-module")).markedArtificial() + } + } + + val node = buildRootNode(storageManager, 0) + goodTransformer(node) + + assertEquals(CirName.create("artificial-module"), node.modules.values.single().targetDeclarations.single()?.name) + } +} \ No newline at end of file