diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/utils.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/utils.kt index daa06f8c7a4..67b88d720e1 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/utils.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/utils.kt @@ -85,3 +85,32 @@ internal tailrec fun computeExpandedType(underlyingType: CirClassOrTypeAliasType @Suppress("unused", "NOTHING_TO_INLINE") internal inline fun CirDeclaration.unsupported(): Nothing = error("This method should never be called on ${this::class.java}, $this") + +internal fun CirClassOrTypeAliasType.withParentArguments( + parentArguments: List, parentIsMarkedNullable: Boolean +): CirClassOrTypeAliasType { + val newIsMarkedNullable = isMarkedNullable || parentIsMarkedNullable + + val newArguments = arguments.map { oldArgument -> + if (oldArgument is CirRegularTypeProjection) { + val oldArgumentType = oldArgument.type + if (oldArgumentType is CirTypeParameterType) { + return@map parentArguments[oldArgumentType.index] + } + } + oldArgument + } + + return when (val newUnderlyingType = makeNullableIfNecessary(newIsMarkedNullable).withArguments(newArguments)) { + this -> this + is CirClassType -> newUnderlyingType + is CirTypeAliasType -> newUnderlyingType.withUnderlyingType( + newUnderlyingType.underlyingType.withParentArguments(newArguments, newIsMarkedNullable) + ) + } +} + +internal tailrec fun CirClassOrTypeAliasType.expandedType(): CirClassType = when (this) { + is CirClassType -> this + is CirTypeAliasType -> this.underlyingType.expandedType() +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/ClassOrTypeAliasTypeCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/ClassOrTypeAliasTypeCommonizer.kt index dbb9f5f437d..9cbaef4a06f 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/ClassOrTypeAliasTypeCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/ClassOrTypeAliasTypeCommonizer.kt @@ -92,44 +92,51 @@ internal class ClassOrTypeAliasTypeCommonizer( } } + @Suppress("KotlinConstantConditions") // Improved readability private fun selectClassifierId(types: List): CirEntityId? { types.singleDistinctValueOrNull { it.classifierId }?.let { return it } - if (typeCommonizer.options.enableTypeAliasSubstitution) { - val commonId = types.singleDistinctValueOrNull { - classifiers.commonClassifierIdResolver.findCommonId(it.classifierId) - } ?: return null + val forwardSubstitutionAllowed = typeCommonizer.options.enableForwardTypeAliasSubstitution + val backwardsSubstitutionAllowed = typeCommonizer.options.enableBackwardsTypeAliasSubstitution - return commonId.aliases.maxByOrNull { candidate -> types.count { it.classifierId == candidate } }!! - } else return null - } -} - -private fun CirClassOrTypeAliasType.withParentArguments( - parentArguments: List, parentIsMarkedNullable: Boolean -): CirClassOrTypeAliasType { - val newIsMarkedNullable = isMarkedNullable || parentIsMarkedNullable - - val newArguments = arguments.map { oldArgument -> - if (oldArgument is CirRegularTypeProjection) { - val oldArgumentType = oldArgument.type - if (oldArgumentType is CirTypeParameterType) { - return@map parentArguments[oldArgumentType.index] - } + /* No substitution allowed in any direction */ + if (!forwardSubstitutionAllowed && !backwardsSubstitutionAllowed) { + return null } - oldArgument + + val commonId = types.singleDistinctValueOrNull { + classifiers.commonClassifierIdResolver.findCommonId(it.classifierId) + } ?: return null + + val candidates = when { + forwardSubstitutionAllowed && backwardsSubstitutionAllowed -> commonId.aliases + + /* Only forward substitutions allowed -> Only substitute with any underlying type */ + forwardSubstitutionAllowed -> commonId.aliases.filter { candidate -> + types.all { type -> type.isAnyUnderlyingClassifier(candidate) } + } + + /* Only backward substitutions allowed -> Only substitute with any typealias pointing to this types */ + backwardsSubstitutionAllowed -> commonId.aliases.filter { candidate -> + types.none { type -> type.isAnyUnderlyingClassifier(candidate) } + } + + else -> error("Failed to select classifierId: Unexpected when branch") + } + + if (candidates.isEmpty()) return null + + return candidates.maxByOrNull { candidate -> types.count { it.classifierId == candidate } }!! } - return when (val newUnderlyingType = makeNullableIfNecessary(newIsMarkedNullable).withArguments(newArguments)) { - this -> this - is CirClassType -> newUnderlyingType - is CirTypeAliasType -> newUnderlyingType.withUnderlyingType( - newUnderlyingType.underlyingType.withParentArguments(newArguments, newIsMarkedNullable) - ) + private fun CirClassOrTypeAliasType.isAnyUnderlyingClassifier(classifierId: CirEntityId): Boolean { + return when (this) { + is CirClassType -> false + is CirTypeAliasType -> this.isAnyUnderlyingClassifier(classifierId) + } + } + + private fun CirTypeAliasType.isAnyUnderlyingClassifier(classifierId: CirEntityId): Boolean { + return underlyingType.classifierId == classifierId || underlyingType.isAnyUnderlyingClassifier(classifierId) } } - -internal tailrec fun CirClassOrTypeAliasType.expandedType(): CirClassType = when (this) { - is CirClassType -> this - is CirTypeAliasType -> this.underlyingType.expandedType() -} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeAliasCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeAliasCommonizer.kt index 978f1e185c8..ca88fe68d5b 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeAliasCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeAliasCommonizer.kt @@ -18,7 +18,7 @@ class TypeAliasCommonizer( ) : NullableSingleInvocationCommonizer { private val typeCommonizer = typeCommonizer.withOptions { - withTypeAliasSubstitutionEnabled(false).withOptimisticNumberTypeCommonizationEnabled(true) + withBackwardsTypeAliasSubstitutionEnabled(false).withOptimisticNumberTypeCommonizationEnabled(true) } override fun invoke(values: List): CirTypeAlias? { 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 0b4588639f9..8c5ae9a4f89 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeCommonizer.kt @@ -36,7 +36,8 @@ class TypeCommonizer( data class Options( val enableOptimisticNumberTypeCommonization: Boolean = false, val enableCovariantNullabilityCommonization: Boolean = false, - val enableTypeAliasSubstitution: Boolean = true + val enableForwardTypeAliasSubstitution: Boolean = true, + val enableBackwardsTypeAliasSubstitution: Boolean = true, ) { fun withOptimisticNumberTypeCommonizationEnabled(enabled: Boolean = true): Options { @@ -49,9 +50,18 @@ class TypeCommonizer( else copy(enableCovariantNullabilityCommonization = enabled) } + fun withForwardTypeAliasSubstitutionEnabled(enabled: Boolean = true): Options { + return if (enableForwardTypeAliasSubstitution == enabled) this + else copy(enableForwardTypeAliasSubstitution = enabled) + } + + fun withBackwardsTypeAliasSubstitutionEnabled(enabled: Boolean = true): Options { + return if (enableBackwardsTypeAliasSubstitution == enabled) this + else copy(enableBackwardsTypeAliasSubstitution = enabled) + } + fun withTypeAliasSubstitutionEnabled(enabled: Boolean = true): Options { - return if (enableTypeAliasSubstitution == enabled) this - else copy(enableTypeAliasSubstitution = enabled) + return withForwardTypeAliasSubstitutionEnabled(enabled).withBackwardsTypeAliasSubstitutionEnabled(enabled) } companion object { diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/UnderscoredTypeAliasTypeSubstitutor.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/UnderscoredTypeAliasTypeSubstitutor.kt index b9fe5dabb55..34f1752fa75 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/UnderscoredTypeAliasTypeSubstitutor.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/transformer/UnderscoredTypeAliasTypeSubstitutor.kt @@ -6,11 +6,7 @@ package org.jetbrains.kotlin.commonizer.transformer import org.jetbrains.kotlin.commonizer.TargetDependent -import org.jetbrains.kotlin.commonizer.cir.CirEntityId -import org.jetbrains.kotlin.commonizer.cir.CirName -import org.jetbrains.kotlin.commonizer.cir.CirType -import org.jetbrains.kotlin.commonizer.cir.CirTypeAliasType -import org.jetbrains.kotlin.commonizer.core.expandedType +import org.jetbrains.kotlin.commonizer.cir.* import org.jetbrains.kotlin.commonizer.mergedtree.CirClassifierIndex import org.jetbrains.kotlin.commonizer.mergedtree.CirTypeSubstitutor import org.jetbrains.kotlin.commonizer.mergedtree.findTypeAlias diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalOptimisticNumbersTypeCommonizerTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalOptimisticNumbersTypeCommonizerTest.kt index 2abd859c7d3..f12ac9722b3 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalOptimisticNumbersTypeCommonizerTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalOptimisticNumbersTypeCommonizerTest.kt @@ -618,7 +618,7 @@ class HierarchicalOptimisticNumbersTypeCommonizerTest : AbstractInlineSourcesCom @UnsafeNumber(["a: kotlin.Int", "b: kotlin.Long"]) typealias B = A @UnsafeNumber(["a: kotlin.Int", "b: kotlin.Long"]) - typealias X = Int + typealias X = A """.trimIndent() ) }