[Commonizer] Prototype: Commonize TypeAliases with Classes

^KT-45992 Fixed
^KT-46061 Fixed
This commit is contained in:
sebastian.sellmair
2021-05-28 18:12:32 +02:00
committed by Space
parent b394dde339
commit 8272564ca0
23 changed files with 615 additions and 155 deletions
@@ -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)
@@ -18,6 +18,8 @@ interface CirClassConstructor :
val isPrimary: Boolean val isPrimary: Boolean
override val containingClass: CirContainingClass // non-nullable override val containingClass: CirContainingClass // non-nullable
override fun withContainingClass(containingClass: CirContainingClass): CirClassConstructor
companion object { companion object {
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun create( inline fun create(
@@ -48,4 +50,8 @@ data class CirClassConstructorImpl(
override var valueParameters: List<CirValueParameter>, override var valueParameters: List<CirValueParameter>,
override var hasStableParameterNames: Boolean, override var hasStableParameterNames: Boolean,
override val isPrimary: Boolean override val isPrimary: Boolean
) : CirClassConstructor ) : CirClassConstructor {
override fun withContainingClass(containingClass: CirContainingClass): CirClassConstructor {
return copy(containingClass = containingClass)
}
}
@@ -41,8 +41,13 @@ interface CirHasModality {
val modality: Modality val modality: Modality
} }
interface CirMaybeCallableMemberOfClass { interface CirMaybeCallableMemberOfClass {
val containingClass: CirContainingClass? // null assumes no containing class val containingClass: CirContainingClass? // null assumes no containing class
fun withContainingClass(containingClass: CirContainingClass): CirMaybeCallableMemberOfClass = object : CirMaybeCallableMemberOfClass {
override val containingClass: CirContainingClass = containingClass
}
} }
/** /**
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.descriptors.Visibility
interface CirFunction : CirFunctionOrProperty, CirCallableMemberWithParameters { interface CirFunction : CirFunctionOrProperty, CirCallableMemberWithParameters {
val modifiers: CirFunctionModifiers val modifiers: CirFunctionModifiers
override fun withContainingClass(containingClass: CirContainingClass): CirFunction
companion object { companion object {
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun create( inline fun create(
@@ -57,4 +59,8 @@ data class CirFunctionImpl(
override val returnType: CirType, override val returnType: CirType,
override val kind: CallableMemberDescriptor.Kind, override val kind: CallableMemberDescriptor.Kind,
override val modifiers: CirFunctionModifiers override val modifiers: CirFunctionModifiers
) : CirFunction ) : CirFunction {
override fun withContainingClass(containingClass: CirContainingClass): CirFunction {
return copy(containingClass = containingClass)
}
}
@@ -21,6 +21,8 @@ interface CirProperty : CirFunctionOrProperty, CirLiftedUpDeclaration {
val delegateFieldAnnotations: List<CirAnnotation> val delegateFieldAnnotations: List<CirAnnotation>
val compileTimeInitializer: CirConstantValue val compileTimeInitializer: CirConstantValue
override fun withContainingClass(containingClass: CirContainingClass): CirProperty
companion object { companion object {
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun create( inline fun create(
@@ -90,4 +92,8 @@ data class CirPropertyImpl(
) : CirProperty { ) : CirProperty {
// const property in "common" fragment is already lifted up // const property in "common" fragment is already lifted up
override val isLiftedUp get() = isConst override val isLiftedUp get() = isConst
override fun withContainingClass(containingClass: CirContainingClass): CirProperty {
return copy(containingClass = containingClass)
}
} }
@@ -16,26 +16,21 @@ import org.jetbrains.kotlin.descriptors.Modality
* *
* Secondary (less optimistic) branch: * Secondary (less optimistic) branch:
* - Make sure that all TAs are identical, so the resulting TA can be lifted up into "common" fragment. * - 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<CirTypeAlias, CirClassifier>() { class TypeAliasCommonizer(classifiers: CirKnownClassifiers) : AbstractStandardCommonizer<CirTypeAlias, CirClassifier>() {
private val primary = TypeAliasShortCircuitingCommonizer(classifiers) private val primary = TypeAliasShortCircuitingCommonizer(classifiers)
private val secondary = TypeAliasLiftingUpCommonizer(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 initialize(first: CirTypeAlias) = Unit
override fun doCommonizeWith(next: CirTypeAlias): Boolean { override fun doCommonizeWith(next: CirTypeAlias): Boolean {
val primaryResult = primary.commonizeWith(next) val primaryResult = primary.commonizeWith(next)
val secondaryResult = secondary.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! // 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) { override fun initialize(first: CirTypeAlias) {
name = first.name name = first.name
} }
@@ -106,43 +98,3 @@ private class TypeAliasLiftingUpCommonizer(classifiers: CirKnownClassifiers) : A
&& underlyingType.commonizeWith(next.underlyingType) && underlyingType.commonizeWith(next.underlyingType)
&& visibility.commonizeWith(next) && visibility.commonizeWith(next)
} }
private class TypeAliasExpectClassCommonizer : AbstractStandardCommonizer<CirTypeAlias, CirClass>() {
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)
}
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.commonizer.core
import org.jetbrains.kotlin.commonizer.cir.* 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.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.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.mergedtree.CirKnownClassifiers
import org.jetbrains.kotlin.commonizer.utils.isUnderKotlinNativeSyntheticPackages import org.jetbrains.kotlin.commonizer.utils.isUnderKotlinNativeSyntheticPackages
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
@@ -203,16 +204,9 @@ private fun commonizeClass(classId: CirEntityId, classifiers: CirKnownClassifier
return true return true
} }
return when (val node = classifiers.commonizedNodes.classNode(classId)) { // Looking for a a node that provides a non-null (successfully commonized) classifier declaration
null -> { return (classifiers.commonizedNodes.classNode(classId)?.commonDeclaration?.invoke()
// No node means that the type alias was not subject for commonization. It is missing in some target(s) => not commonized. ?: classifiers.commonizedNodes.typeAliasNode(classId)?.commonDeclaration?.invoke()) != null
false
}
else -> {
// Common declaration in node is not null -> successfully commonized.
(node.commonDeclaration() != null)
}
}
} }
private fun commonizeTypeAlias(typeAliasId: CirEntityId, classifiers: CirKnownClassifiers): CommonizedTypeAliasAnswer { 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 SUCCESS_FROM_DEPENDENCY_LIBRARY
} }
return when (val node = classifiers.commonizedNodes.typeAliasNode(typeAliasId)) { val typeAliasNode = classifiers.commonizedNodes.typeAliasNode(typeAliasId)
null -> { val classNode = classifiers.commonizedNodes.classNode(typeAliasId)
// 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 if (typeAliasNode == null && classNode == null) {
} return FAILURE_MISSING_IN_SOME_TARGET
else -> {
// Common declaration in node is not null -> successfully commonized.
CommonizedTypeAliasAnswer.create(node.commonDeclaration())
}
} }
return create(typeAliasNode?.commonDeclaration?.invoke() ?: classNode?.commonDeclaration?.invoke())
} }
private class CommonizedTypeAliasAnswer(val commonized: Boolean, val commonClassifier: CirClassifier?) { private class CommonizedTypeAliasAnswer(val commonized: Boolean, val commonClassifier: CirClassifier?) {
@@ -18,8 +18,7 @@ internal tailrec fun computeSuitableUnderlyingType(
return when (underlyingType) { return when (underlyingType) {
is CirClassType -> underlyingType.withCommonizedArguments(classifiers) is CirClassType -> underlyingType.withCommonizedArguments(classifiers)
is CirTypeAliasType -> if (classifiers.commonDependencies.hasClassifier(underlyingType.classifierId)) is CirTypeAliasType -> if (classifiers.commonDependencies.hasClassifier(underlyingType.classifierId))
underlyingType.withCommonizedArguments(classifiers) underlyingType.withCommonizedArguments(classifiers) else
else
computeSuitableUnderlyingType(classifiers, underlyingType.underlyingType) computeSuitableUnderlyingType(classifiers, underlyingType.underlyingType)
} }
} }
@@ -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.CirNode.Companion.targetIndices
import org.jetbrains.kotlin.commonizer.mergedtree.CirRootNode import org.jetbrains.kotlin.commonizer.mergedtree.CirRootNode
import org.jetbrains.kotlin.commonizer.metadata.CirTreeSerializer.serializeSingleTarget 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.CirTreeRoot
import org.jetbrains.kotlin.commonizer.tree.assembleCirTree import org.jetbrains.kotlin.commonizer.tree.assembleCirTree
import org.jetbrains.kotlin.commonizer.tree.deserializeCirTree import org.jetbrains.kotlin.commonizer.tree.deserializeCirTree
@@ -50,6 +52,7 @@ private fun commonize(
) )
val mergedTree = merge(storageManager, classifiers, cirTrees) ?: return null val mergedTree = merge(storageManager, classifiers, cirTrees) ?: return null
InlineTypeAliasCirNodeTransformer(storageManager, classifiers).invoke(mergedTree)
mergedTree.accept(CommonizationVisitor(classifiers, mergedTree), Unit) mergedTree.accept(CommonizationVisitor(classifiers, mergedTree), Unit)
parameters.logProgress("Commonized declarations for $target") parameters.logProgress("Commonized declarations for $target")
@@ -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<CirDeclaration>
) : CommonizerCondition {
override fun allowCommonization(): Boolean = parentNodeCommonDeclaration() != null
}
@@ -20,6 +20,7 @@ internal fun buildRootNode(
): CirRootNode = buildNode( ): CirRootNode = buildNode(
storageManager = storageManager, storageManager = storageManager,
size = size, size = size,
commonizerCondition = CommonizerCondition.none(),
commonizerProducer = ::RootCommonizer, commonizerProducer = ::RootCommonizer,
nodeProducer = ::CirRootNode nodeProducer = ::CirRootNode
) )
@@ -30,6 +31,7 @@ internal fun buildModuleNode(
): CirModuleNode = buildNode( ): CirModuleNode = buildNode(
storageManager = storageManager, storageManager = storageManager,
size = size, size = size,
commonizerCondition = CommonizerCondition.none(),
commonizerProducer = ::ModuleCommonizer, commonizerProducer = ::ModuleCommonizer,
nodeProducer = ::CirModuleNode nodeProducer = ::CirModuleNode
) )
@@ -40,6 +42,7 @@ internal fun buildPackageNode(
): CirPackageNode = buildNode( ): CirPackageNode = buildNode(
storageManager = storageManager, storageManager = storageManager,
size = size, size = size,
commonizerCondition = CommonizerCondition.none(),
commonizerProducer = ::PackageCommonizer, commonizerProducer = ::PackageCommonizer,
nodeProducer = ::CirPackageNode nodeProducer = ::CirPackageNode
) )
@@ -48,11 +51,11 @@ internal fun buildPropertyNode(
storageManager: StorageManager, storageManager: StorageManager,
size: Int, size: Int,
classifiers: CirKnownClassifiers, classifiers: CirKnownClassifiers,
parentCommonDeclaration: NullableLazyValue<*>? condition: CommonizerCondition = CommonizerCondition.none(),
): CirPropertyNode = buildNode( ): CirPropertyNode = buildNode(
storageManager = storageManager, storageManager = storageManager,
size = size, size = size,
parentCommonDeclaration = parentCommonDeclaration, commonizerCondition = condition,
commonizerProducer = { PropertyCommonizer(classifiers) }, commonizerProducer = { PropertyCommonizer(classifiers) },
nodeProducer = ::CirPropertyNode nodeProducer = ::CirPropertyNode
) )
@@ -61,11 +64,11 @@ internal fun buildFunctionNode(
storageManager: StorageManager, storageManager: StorageManager,
size: Int, size: Int,
classifiers: CirKnownClassifiers, classifiers: CirKnownClassifiers,
parentCommonDeclaration: NullableLazyValue<*>? condition: CommonizerCondition = CommonizerCondition.none(),
): CirFunctionNode = buildNode( ): CirFunctionNode = buildNode(
storageManager = storageManager, storageManager = storageManager,
size = size, size = size,
parentCommonDeclaration = parentCommonDeclaration, commonizerCondition = condition,
commonizerProducer = { FunctionCommonizer(classifiers) }, commonizerProducer = { FunctionCommonizer(classifiers) },
nodeProducer = ::CirFunctionNode nodeProducer = ::CirFunctionNode
) )
@@ -74,12 +77,12 @@ internal fun buildClassNode(
storageManager: StorageManager, storageManager: StorageManager,
size: Int, size: Int,
classifiers: CirKnownClassifiers, classifiers: CirKnownClassifiers,
parentCommonDeclaration: NullableLazyValue<*>?, condition: CommonizerCondition = CommonizerCondition.none(),
classId: CirEntityId classId: CirEntityId
): CirClassNode = buildNode( ): CirClassNode = buildNode(
storageManager = storageManager, storageManager = storageManager,
size = size, size = size,
parentCommonDeclaration = parentCommonDeclaration, commonizerCondition = condition,
commonizerProducer = { ClassCommonizer(classifiers) }, commonizerProducer = { ClassCommonizer(classifiers) },
recursionMarker = CirClassRecursionMarker, recursionMarker = CirClassRecursionMarker,
nodeProducer = { targetDeclarations, commonDeclaration -> nodeProducer = { targetDeclarations, commonDeclaration ->
@@ -89,15 +92,16 @@ internal fun buildClassNode(
} }
) )
internal fun buildClassConstructorNode( internal fun buildClassConstructorNode(
storageManager: StorageManager, storageManager: StorageManager,
size: Int, size: Int,
classifiers: CirKnownClassifiers, classifiers: CirKnownClassifiers,
parentCommonDeclaration: NullableLazyValue<*>? condition: ParentNodeCommonizerCondition,
): CirClassConstructorNode = buildNode( ): CirClassConstructorNode = buildNode(
storageManager = storageManager, storageManager = storageManager,
size = size, size = size,
parentCommonDeclaration = parentCommonDeclaration, commonizerCondition = condition,
commonizerProducer = { ClassConstructorCommonizer(classifiers) }, commonizerProducer = { ClassConstructorCommonizer(classifiers) },
nodeProducer = ::CirClassConstructorNode nodeProducer = ::CirClassConstructorNode
) )
@@ -110,6 +114,7 @@ internal fun buildTypeAliasNode(
): CirTypeAliasNode = buildNode( ): CirTypeAliasNode = buildNode(
storageManager = storageManager, storageManager = storageManager,
size = size, size = size,
commonizerCondition = CommonizerCondition.none(),
commonizerProducer = { TypeAliasCommonizer(classifiers) }, commonizerProducer = { TypeAliasCommonizer(classifiers) },
recursionMarker = CirClassifierRecursionMarker, recursionMarker = CirClassifierRecursionMarker,
nodeProducer = { targetDeclarations, commonDeclaration -> nodeProducer = { targetDeclarations, commonDeclaration ->
@@ -122,14 +127,14 @@ internal fun buildTypeAliasNode(
private fun <T : CirDeclaration, R : CirDeclaration, N : CirNode<T, R>> buildNode( private fun <T : CirDeclaration, R : CirDeclaration, N : CirNode<T, R>> buildNode(
storageManager: StorageManager, storageManager: StorageManager,
size: Int, size: Int,
parentCommonDeclaration: NullableLazyValue<*>? = null, commonizerCondition: CommonizerCondition,
commonizerProducer: () -> Commonizer<T, R>, commonizerProducer: () -> Commonizer<T, R>,
recursionMarker: R? = null, recursionMarker: R? = null,
nodeProducer: (CommonizedGroup<T>, NullableLazyValue<R>) -> N nodeProducer: (CommonizedGroup<T>, NullableLazyValue<R>) -> N
): N { ): N {
val targetDeclarations = CommonizedGroup<T>(size) val targetDeclarations = CommonizedGroup<T>(size)
val commonComputable = { commonize(parentCommonDeclaration, targetDeclarations, commonizerProducer()) } val commonComputable = { commonize(commonizerCondition, targetDeclarations, commonizerProducer()) }
val commonLazyValue = if (recursionMarker != null) val commonLazyValue = if (recursionMarker != null)
storageManager.createRecursionTolerantNullableLazyValue(commonComputable, recursionMarker) storageManager.createRecursionTolerantNullableLazyValue(commonComputable, recursionMarker)
@@ -153,14 +158,13 @@ internal fun <T : Any, R> commonize(
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
private inline fun <T : Any, R> commonize( private inline fun <T : Any, R> commonize(
parentCommonDeclaration: NullableLazyValue<*>?, commonizerCondition: CommonizerCondition,
targetDeclarations: CommonizedGroup<T>, targetDeclarations: CommonizedGroup<T>,
commonizer: Commonizer<T, R> commonizer: Commonizer<T, R>
): R? { ): R? {
if (parentCommonDeclaration != null && parentCommonDeclaration.invoke() == null) { if (commonizerCondition.allowCommonization()) {
// don't commonize declaration if it's parent failed to commonize return commonize(targetDeclarations, commonizer)
return null
} }
return commonize(targetDeclarations, commonizer) return null
} }
@@ -143,6 +143,7 @@ private class CirTreeSerializationVisitor(
classContext: CirTreeSerializationContext classContext: CirTreeSerializationContext
): KmClass? { ): KmClass? {
val cirClass = classContext.get<CirClass>(node) ?: return null val cirClass = classContext.get<CirClass>(node) ?: return null
val classTypeParametersCount = cirClass.typeParameters.size val classTypeParametersCount = cirClass.typeParameters.size
val fullClassName = classContext.currentPath.toString() val fullClassName = classContext.currentPath.toString()
@@ -403,7 +404,8 @@ internal data class CirTreeSerializationContext(
} }
inline fun <reified T : CirDeclaration> get(node: CirNode<*, *>): T? { inline fun <reified T : CirDeclaration> 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 <reified T : CirDeclaration> get(node: CirNodeWithLiftingUp<*, *>): T? { inline fun <reified T : CirDeclaration> get(node: CirNodeWithLiftingUp<*, *>): T? {
@@ -411,7 +413,7 @@ internal data class CirTreeSerializationContext(
isCommon -> node.commonDeclaration() as T? isCommon -> node.commonDeclaration() as T?
node.isLiftedUp -> null node.isLiftedUp -> null
else -> node.targetDeclarations[targetIndex] as T? else -> node.targetDeclarations[targetIndex] as T?
} }.takeIf { it !is ArtificialCirDeclaration }
} }
companion object { companion object {
@@ -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<MutableCirDeclarationCount, Unit> {
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
}
}
@@ -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"
)
}
}
}
}
}
@@ -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<CirEntityId, CirClassNode>
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()
@@ -52,7 +52,7 @@ internal fun CirNodeWithMembers<*, *>.buildClass(
context: TargetBuildingContext, treeClass: CirTreeClass, parent: CirNode<*, *>? = null context: TargetBuildingContext, treeClass: CirTreeClass, parent: CirNode<*, *>? = null
) { ) {
val classNode = classes.getOrPut(treeClass.clazz.name) { 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 classNode.targetDeclarations[context.targetIndex] = treeClass.clazz
treeClass.functions.forEach { function -> classNode.buildFunction(context, function, classNode) } treeClass.functions.forEach { function -> classNode.buildFunction(context, function, classNode) }
@@ -65,7 +65,7 @@ internal fun CirNodeWithMembers<*, *>.buildFunction(
context: TargetBuildingContext, treeFunction: CirTreeFunction, parent: CirNode<*, *>? = null context: TargetBuildingContext, treeFunction: CirTreeFunction, parent: CirNode<*, *>? = null
) { ) {
val functionNode = functions.getOrPut(treeFunction.approximationKey) { 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 functionNode.targetDeclarations[context.targetIndex] = treeFunction.function
} }
@@ -74,16 +74,16 @@ internal fun CirNodeWithMembers<*, *>.buildProperty(
context: TargetBuildingContext, treeProperty: CirTreeProperty, parent: CirNode<*, *>? = null context: TargetBuildingContext, treeProperty: CirTreeProperty, parent: CirNode<*, *>? = null
) { ) {
val propertyNode = properties.getOrPut(treeProperty.approximationKey) { 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 propertyNode.targetDeclarations[context.targetIndex] = treeProperty.property
} }
internal fun CirClassNode.buildConstructor( internal fun CirClassNode.buildConstructor(
context: TargetBuildingContext, treeConstructor: CirTreeClassConstructor, parent: CirNode<*, *>? context: TargetBuildingContext, treeConstructor: CirTreeClassConstructor, parent: CirNode<*, *>
) { ) {
val constructorNode = constructors.getOrPut(treeConstructor.approximationKey) { 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 constructorNode.targetDeclarations[context.targetIndex] = treeConstructor.constructor
} }
@@ -94,4 +94,3 @@ internal fun CirPackageNode.buildTypeAlias(context: TargetBuildingContext, treeT
} }
typeAliasNode.targetDeclarations[context.targetIndex] = treeTypeAlias.typeAlias typeAliasNode.targetDeclarations[context.targetIndex] = treeTypeAlias.typeAlias
} }
@@ -1,5 +1,4 @@
expect class A() expect class A()
// Lifted up type aliases: // Lifted up type aliases:
typealias B = A // class at the RHS typealias B = A // class at the RHS
typealias C = A // TA at the RHS, expanded to the same class 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 expect class V // different nullability of the RHS class
typealias W = A // same nullability of the RHS TA 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 typealias Y = V // TA at the RHS with the different nullability of own RHS
// Supertypes: // Supertypes:
expect class FILE : kotlinx.cinterop.CStructVar expect class FILE expect constructor(): kotlinx.cinterop.CStructVar
typealias uuid_t = kotlinx.cinterop.CArrayPointer<kotlinx.cinterop.UByteVar> typealias uuid_t = kotlinx.cinterop.CArrayPointer<kotlinx.cinterop.UByteVar>
typealias __darwin_uuid_t = kotlinx.cinterop.CArrayPointer<kotlinx.cinterop.UByteVar> typealias __darwin_uuid_t = kotlinx.cinterop.CArrayPointer<kotlinx.cinterop.UByteVar>
@@ -6,18 +6,18 @@ public typealias E1 = A1
protected typealias E2 = A1 protected typealias E2 = A1
internal typealias E3 = A1 internal typealias E3 = A1
public expect class E4 public expect class E4()
protected typealias F1 = A1 protected typealias F1 = A1
public expect class F2 public expect class F2()
public expect class F3 public expect class F3()
internal typealias G1 = A1 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 I1 = A1
public typealias I2 = B1 public typealias I2 = B1
@@ -19,4 +19,4 @@ expect class AnnotatedClass(value: String) {
} }
typealias AnnotatedLiftedUpTypeAlias = AnnotatedClass typealias AnnotatedLiftedUpTypeAlias = AnnotatedClass
expect class AnnotatedNonLiftedUpTypeAlias expect class AnnotatedNonLiftedUpTypeAlias expect constructor(expect val value: String)
@@ -272,14 +272,6 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
shouldFailOnFirstVariant = true 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 @Test
// why success: short-circuiting & lifting up // why success: short-circuiting & lifting up
fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass1() = doTestSuccess( fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass1() = doTestSuccess(
@@ -338,28 +330,6 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
} }
) )
@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 @Test
// why success: types with the same nullability are treated as equal // why success: types with the same nullability are treated as equal
fun taTypesInKotlinPackageWithSameNullability1() = doTestSuccess( fun taTypesInKotlinPackageWithSameNullability1() = doTestSuccess(
@@ -432,30 +402,16 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
fun taTypesInUserPackageWithDifferentNullability1() = doTestFailure( 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 = 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) @Test(expected = IllegalCommonizerStateException::class)
fun taTypesInUserPackageWithDifferentNullability2() = doTestFailure( 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 = 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") } mockTAType("org/sample/FooAlias", nullable = false) { mockClassType("org/sample/Foo") },
) shouldFailOnFirstVariant = true
@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) }
) )
private fun prepareCache(variants: Array<out CirClassOrTypeAliasType>) { private fun prepareCache(variants: Array<out CirClassOrTypeAliasType>) {
@@ -469,7 +425,7 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
storageManager = LockBasedStorageManager.NO_LOCKS, storageManager = LockBasedStorageManager.NO_LOCKS,
size = variants.size, size = variants.size,
classifiers = classifiers, classifiers = classifiers,
parentCommonDeclaration = null, condition = CommonizerCondition.none(),
classId = type.classifierId classId = type.classifierId
) )
} }
@@ -104,4 +104,141 @@ class HierarchicalClassAndTypeAliasCommonizationTest : AbstractInlineSourcesComm
""".trimIndent() """.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()
)
}
} }
@@ -111,7 +111,7 @@ class HierarchicalTypeAliasCommonizationTest : AbstractInlineSourcesCommonizatio
"(e,f)", """expect class x expect constructor()""" "(e,f)", """expect class x expect constructor()"""
) )
result.assertCommonized("((a,b), (c,d))", """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""") result.assertCommonized("(((a,b), (c,d)), (e,f))", """expect class x expect constructor()""")
} }
} }
@@ -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<AssertionError> { badTransformer(node) }
}
@Test
fun `when attaching non-artificial declarations`() {
val goodTransformer = CirNodeTransformer { root ->
assertIs<Checked>(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)
}
}