diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/typeAliasDescriptors.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/typeAliasDescriptors.kt index 185f06f766a..3531ec0d6ec 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/typeAliasDescriptors.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/typeAliasDescriptors.kt @@ -70,10 +70,14 @@ private fun CirTypeAlias.buildDescriptor( typeAliasDescriptor ) + val lazyUnderlyingType = storageManager.createLazyValue { + underlyingType.buildType(targetComponents, typeParameterResolver) + } + typeAliasDescriptor.initialize( declaredTypeParameters = declaredTypeParameters, - underlyingType = storageManager.createLazyValue { underlyingType.buildType(targetComponents, typeParameterResolver) }, - expandedType = storageManager.createLazyValue { expandedType.buildType(targetComponents, typeParameterResolver) } + underlyingType = lazyUnderlyingType, + expandedType = lazyUnderlyingType ) // cache created type alias descriptor: diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeAliasFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeAliasFactory.kt index 4d27fe7a125..cadbbe43ac9 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeAliasFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeAliasFactory.kt @@ -22,7 +22,7 @@ object CirTypeAliasFactory { typeParameters = source.declaredTypeParameters.map(CirTypeParameterFactory::create), visibility = source.visibility, underlyingType = CirTypeFactory.create(source.underlyingType), - expandedType = CirTypeFactory.create(source.expandedType) + expandedType = CirTypeFactory.create(source.expandedType, useAbbreviation = false) ) @Suppress("NOTHING_TO_INLINE") diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeFactory.kt index cd471766cc3..337254b6a41 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeFactory.kt @@ -25,21 +25,22 @@ object CirTypeFactory { } } - fun create(source: SimpleType): CirSimpleType { - val abbreviation: SimpleType = (source as? AbbreviatedType)?.abbreviation ?: source - val classifierDescriptor: ClassifierDescriptor = abbreviation.declarationDescriptor + fun create(source: SimpleType, useAbbreviation: Boolean = true): CirSimpleType { + @Suppress("NAME_SHADOWING") + val source = if (useAbbreviation && source is AbbreviatedType) source.abbreviation else source + val classifierDescriptor: ClassifierDescriptor = source.declarationDescriptor return create( classifierId = CirClassifierIdFactory.create(classifierDescriptor), visibility = (classifierDescriptor as? ClassifierDescriptorWithTypeParameters)?.visibility ?: Visibilities.UNKNOWN, - arguments = abbreviation.arguments.map { projection -> + arguments = source.arguments.map { projection -> CirTypeProjection( projectionKind = projection.projectionKind, isStarProjection = projection.isStarProjection, type = create(projection.type) ) }, - isMarkedNullable = abbreviation.isMarkedNullable + isMarkedNullable = source.isMarkedNullable ) } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/impl/CirTypeAliasImpl.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/impl/CirTypeAliasImpl.kt index ce6a4faa5ce..dbe26a2dcb1 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/impl/CirTypeAliasImpl.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/impl/CirTypeAliasImpl.kt @@ -18,7 +18,7 @@ data class CirTypeAliasImpl( override val typeParameters: List, override val visibility: Visibility, override val underlyingType: CirSimpleType, - override val expandedType: CirSimpleType + override val expandedType: CirSimpleType // only for commonization algorithm; does not participate in building resulting declarations ) : CirTypeAlias { // any TA in "common" fragment is already lifted up override val isLiftedUp get() = true diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/TypeAliasCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/TypeAliasCommonizer.kt index b5782965298..5180f86637b 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/TypeAliasCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/TypeAliasCommonizer.kt @@ -14,30 +14,67 @@ import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirClassifiersCach import org.jetbrains.kotlin.name.Name /** - * Main (optimistic) branch: + * Primary (optimistic) branch: + * - Make sure that all TAs expand to the same type, so the resulting TA can be short-circuited and lifted up into "common" fragment. + * + * Secondary (less optimistic) branch: * - Make sure that all TAs are identical, so the resulting TA can be lifted up into "common" fragment. * - * Secondary (backup) branch: + * Tertiary (backup) branch: * - Produce an "expect class" for "common" fragment and the corresponding "actual typealias" declarations for each platform fragment. */ class TypeAliasCommonizer(cache: CirClassifiersCache) : AbstractStandardCommonizer() { - private val main = TypeAliasLiftingUpCommonizer(cache) - private val secondary = TypeAliasExpectClassCommonizer() + private val primary = TypeAliasShortCircuitingCommonizer(cache) + private val secondary = TypeAliasLiftingUpCommonizer(cache) + private val tertiary = TypeAliasExpectClassCommonizer() - override fun commonizationResult(): CirClassifier = main.resultOrNull ?: secondary.result + override fun commonizationResult(): CirClassifier = primary.resultOrNull ?: secondary.resultOrNull ?: tertiary.result override fun initialize(first: CirTypeAlias) = Unit @Suppress("ReplaceNegatedIsEmptyWithIsNotEmpty") override fun doCommonizeWith(next: CirTypeAlias): Boolean { - val mainResult = main.commonizeWith(next) + 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 mainResult || secondaryResult + return primaryResult || secondaryResult || tertiaryResult } } +private class TypeAliasShortCircuitingCommonizer(cache: CirClassifiersCache) : AbstractStandardCommonizer() { + private lateinit var name: Name + private val expandedType = TypeCommonizer(cache) + private val visibility = VisibilityCommonizer.lowering() + + override fun commonizationResult(): CirTypeAlias { + val expandedType = expandedType.result as CirSimpleType + + return CirTypeAliasFactory.create( + annotations = emptyList(), + name = name, + typeParameters = emptyList(), + visibility = visibility.result, + underlyingType = expandedType, // pass expanded type as underlying type + expandedType = expandedType + ) + } + + val resultOrNull: CirTypeAlias? + get() = if (hasResult) commonizationResult() else null + + override fun initialize(first: CirTypeAlias) { + name = first.name + } + + override fun doCommonizeWith(next: CirTypeAlias) = + next.typeParameters.isEmpty() // short-circuiting of TAs with type parameters is too complex case, consider implementing it later + && next.expandedType.arguments.isEmpty() // short-circuiting of TAs with type arguments in expanded type is too complex case + && expandedType.commonizeWith(next.expandedType) + && visibility.commonizeWith(next) +} + private class TypeAliasLiftingUpCommonizer(cache: CirClassifiersCache) : AbstractStandardCommonizer() { private lateinit var name: Name private val typeParameters = TypeParameterListCommonizer(cache) 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 d1d838d3f97..3b19acb9b33 100644 --- a/native/commonizer/testData/classifierCommonization/typeAliases/commonized/common/package_root.kt +++ b/native/commonizer/testData/classifierCommonization/typeAliases/commonized/common/package_root.kt @@ -2,7 +2,12 @@ expect class A() // Lifted up type aliases: typealias B = A // class at the RHS -typealias C = B // TA at the RHS +typealias C = A // TA at the RHS, expanded to the same class +typealias C2 = A // 2x TA at the RHS, expanded to the same class +typealias C3 = A // 3x TA at the RHS, expanded to the same class + +typealias D = A // class/TA expanded to the same class at the RHS +typealias E = A // different TAs expanded to the same class at the RHS typealias F = List // parameterized type at the RHS typealias H = List // TA with own parameters @@ -21,5 +26,5 @@ expect class T // Nullability: typealias U = A // same nullability of the RHS class expect class V // different nullability of the RHS class -typealias W = U // same nullability of the RHS TA +typealias W = A // same nullability of the RHS TA typealias Y = V // TA at the RHS with the different nullability of own RHS diff --git a/native/commonizer/testData/classifierCommonization/typeAliases/commonized/js/package_root.kt b/native/commonizer/testData/classifierCommonization/typeAliases/commonized/js/package_root.kt index 3152cbb7dfd..fb2f7ed4e66 100644 --- a/native/commonizer/testData/classifierCommonization/typeAliases/commonized/js/package_root.kt +++ b/native/commonizer/testData/classifierCommonization/typeAliases/commonized/js/package_root.kt @@ -1,9 +1,6 @@ actual class A actual constructor() // Lifted up type aliases: -typealias D = B // class/TA at the RHS -typealias E = C // different TAs at the RHS - typealias G = List // different parameterized types at the RHS typealias I = List // TAs with own parameters with different names diff --git a/native/commonizer/testData/classifierCommonization/typeAliases/commonized/jvm/package_root.kt b/native/commonizer/testData/classifierCommonization/typeAliases/commonized/jvm/package_root.kt index 2a5f56c0e84..907f7893e33 100644 --- a/native/commonizer/testData/classifierCommonization/typeAliases/commonized/jvm/package_root.kt +++ b/native/commonizer/testData/classifierCommonization/typeAliases/commonized/jvm/package_root.kt @@ -1,9 +1,6 @@ actual class A actual constructor() // Lifted up type aliases: -typealias D = A // class/TA at the RHS -typealias E = B // different TAs at the RHS - typealias G = List // different parameterized types at the RHS typealias I = List // TAs with own parameters with different names diff --git a/native/commonizer/testData/classifierCommonization/typeAliases/original/js/package_root.kt b/native/commonizer/testData/classifierCommonization/typeAliases/original/js/package_root.kt index 2c035911c5b..ea81e6175a0 100644 --- a/native/commonizer/testData/classifierCommonization/typeAliases/original/js/package_root.kt +++ b/native/commonizer/testData/classifierCommonization/typeAliases/original/js/package_root.kt @@ -2,10 +2,12 @@ class A // Lifted up type aliases: typealias B = A // class at the RHS -typealias C = B // TA at the RHS +typealias C = B // TA at the RHS, expanded to the same class +typealias C2 = C // 2x TA at the RHS, expanded to the same class +typealias C3 = C2 // 3x TA at the RHS, expanded to the same class -typealias D = B // class/TA at the RHS -typealias E = C // different TAs at the RHS +typealias D = B // class/TA expanded to the same class at the RHS +typealias E = C // different TAs expanded to the same class at the RHS typealias F = List // parameterized type at the RHS typealias G = List // different parameterized types at the RHS diff --git a/native/commonizer/testData/classifierCommonization/typeAliases/original/jvm/package_root.kt b/native/commonizer/testData/classifierCommonization/typeAliases/original/jvm/package_root.kt index 0c9896c5ecf..d15760dc16a 100644 --- a/native/commonizer/testData/classifierCommonization/typeAliases/original/jvm/package_root.kt +++ b/native/commonizer/testData/classifierCommonization/typeAliases/original/jvm/package_root.kt @@ -2,10 +2,12 @@ class A // Lifted up type aliases: typealias B = A // class at the RHS -typealias C = B // TA at the RHS +typealias C = B // TA at the RHS, expanded to the same class +typealias C2 = C // 2x TA at the RHS, expanded to the same class +typealias C3 = C2 // 3x TA at the RHS, expanded to the same class -typealias D = A // class/TA at the RHS -typealias E = B // different TAs at the RHS +typealias D = A // class/TA expanded to the same class at the RHS +typealias E = B // different TAs expanded to the same class at the RHS typealias F = List // parameterized type at the RHS typealias G = List // different parameterized types at the RHS diff --git a/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/TypeCommonizerTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/TypeCommonizerTest.kt index c0654caa241..40fd9e42177 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/TypeCommonizerTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/TypeCommonizerTest.kt @@ -279,8 +279,13 @@ class TypeCommonizerTest : AbstractCommonizerTest() { mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Bar") } ) - @Test(expected = IllegalCommonizerStateException::class) - fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass1() = doTestFailure( + @Test + // why success: short-circuiting & lifting up + fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass1() = doTestSuccess( + expected = mockTAType("org.sample.FooAlias") { + mockClassType("org.sample.Foo") + }, + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo") }, @@ -289,13 +294,16 @@ class TypeCommonizerTest : AbstractCommonizerTest() { mockTAType("org.sample.FooAliasL2") { mockClassType("org.sample.Foo") } - }, - - shouldFailOnFirstVariant = true + } ) - @Test(expected = IllegalCommonizerStateException::class) - fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass2() = doTestFailure( + @Test + // why success: short-circuiting & lifting up + fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass2() = doTestSuccess( + expected = mockTAType("org.sample.FooAlias") { + mockClassType("org.sample.Foo") + }, + mockTAType("org.sample.FooAlias") { mockTAType("org.sample.FooAliasL2") { mockClassType("org.sample.Foo") @@ -304,9 +312,7 @@ class TypeCommonizerTest : AbstractCommonizerTest() { mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo") - }, - - shouldFailOnFirstVariant = true + } ) @Test