[Commonizer] Short-circuiting of type aliases

^KT-41220
This commit is contained in:
Dmitriy Dolovov
2020-08-19 18:45:30 +07:00
parent 8a84af8a6f
commit d40bca4143
11 changed files with 91 additions and 40 deletions
@@ -70,10 +70,14 @@ private fun CirTypeAlias.buildDescriptor(
typeAliasDescriptor typeAliasDescriptor
) )
val lazyUnderlyingType = storageManager.createLazyValue {
underlyingType.buildType(targetComponents, typeParameterResolver)
}
typeAliasDescriptor.initialize( typeAliasDescriptor.initialize(
declaredTypeParameters = declaredTypeParameters, declaredTypeParameters = declaredTypeParameters,
underlyingType = storageManager.createLazyValue { underlyingType.buildType(targetComponents, typeParameterResolver) }, underlyingType = lazyUnderlyingType,
expandedType = storageManager.createLazyValue { expandedType.buildType(targetComponents, typeParameterResolver) } expandedType = lazyUnderlyingType
) )
// cache created type alias descriptor: // cache created type alias descriptor:
@@ -22,7 +22,7 @@ object CirTypeAliasFactory {
typeParameters = source.declaredTypeParameters.map(CirTypeParameterFactory::create), typeParameters = source.declaredTypeParameters.map(CirTypeParameterFactory::create),
visibility = source.visibility, visibility = source.visibility,
underlyingType = CirTypeFactory.create(source.underlyingType), underlyingType = CirTypeFactory.create(source.underlyingType),
expandedType = CirTypeFactory.create(source.expandedType) expandedType = CirTypeFactory.create(source.expandedType, useAbbreviation = false)
) )
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
@@ -25,21 +25,22 @@ object CirTypeFactory {
} }
} }
fun create(source: SimpleType): CirSimpleType { fun create(source: SimpleType, useAbbreviation: Boolean = true): CirSimpleType {
val abbreviation: SimpleType = (source as? AbbreviatedType)?.abbreviation ?: source @Suppress("NAME_SHADOWING")
val classifierDescriptor: ClassifierDescriptor = abbreviation.declarationDescriptor val source = if (useAbbreviation && source is AbbreviatedType) source.abbreviation else source
val classifierDescriptor: ClassifierDescriptor = source.declarationDescriptor
return create( return create(
classifierId = CirClassifierIdFactory.create(classifierDescriptor), classifierId = CirClassifierIdFactory.create(classifierDescriptor),
visibility = (classifierDescriptor as? ClassifierDescriptorWithTypeParameters)?.visibility ?: Visibilities.UNKNOWN, visibility = (classifierDescriptor as? ClassifierDescriptorWithTypeParameters)?.visibility ?: Visibilities.UNKNOWN,
arguments = abbreviation.arguments.map { projection -> arguments = source.arguments.map { projection ->
CirTypeProjection( CirTypeProjection(
projectionKind = projection.projectionKind, projectionKind = projection.projectionKind,
isStarProjection = projection.isStarProjection, isStarProjection = projection.isStarProjection,
type = create(projection.type) type = create(projection.type)
) )
}, },
isMarkedNullable = abbreviation.isMarkedNullable isMarkedNullable = source.isMarkedNullable
) )
} }
@@ -18,7 +18,7 @@ data class CirTypeAliasImpl(
override val typeParameters: List<CirTypeParameter>, override val typeParameters: List<CirTypeParameter>,
override val visibility: Visibility, override val visibility: Visibility,
override val underlyingType: CirSimpleType, override val underlyingType: CirSimpleType,
override val expandedType: CirSimpleType override val expandedType: CirSimpleType // only for commonization algorithm; does not participate in building resulting declarations
) : CirTypeAlias { ) : CirTypeAlias {
// any TA in "common" fragment is already lifted up // any TA in "common" fragment is already lifted up
override val isLiftedUp get() = true override val isLiftedUp get() = true
@@ -14,30 +14,67 @@ import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirClassifiersCach
import org.jetbrains.kotlin.name.Name 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. * - 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. * - Produce an "expect class" for "common" fragment and the corresponding "actual typealias" declarations for each platform fragment.
*/ */
class TypeAliasCommonizer(cache: CirClassifiersCache) : AbstractStandardCommonizer<CirTypeAlias, CirClassifier>() { class TypeAliasCommonizer(cache: CirClassifiersCache) : AbstractStandardCommonizer<CirTypeAlias, CirClassifier>() {
private val main = TypeAliasLiftingUpCommonizer(cache) private val primary = TypeAliasShortCircuitingCommonizer(cache)
private val secondary = TypeAliasExpectClassCommonizer() 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 override fun initialize(first: CirTypeAlias) = Unit
@Suppress("ReplaceNegatedIsEmptyWithIsNotEmpty") @Suppress("ReplaceNegatedIsEmptyWithIsNotEmpty")
override fun doCommonizeWith(next: CirTypeAlias): Boolean { override fun doCommonizeWith(next: CirTypeAlias): Boolean {
val mainResult = main.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 mainResult || secondaryResult return primaryResult || secondaryResult || tertiaryResult
} }
} }
private class TypeAliasShortCircuitingCommonizer(cache: CirClassifiersCache) : AbstractStandardCommonizer<CirTypeAlias, CirTypeAlias>() {
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<CirTypeAlias, CirTypeAlias>() { private class TypeAliasLiftingUpCommonizer(cache: CirClassifiersCache) : AbstractStandardCommonizer<CirTypeAlias, CirTypeAlias>() {
private lateinit var name: Name private lateinit var name: Name
private val typeParameters = TypeParameterListCommonizer(cache) private val typeParameters = TypeParameterListCommonizer(cache)
@@ -2,7 +2,12 @@ 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 = 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<String> // parameterized type at the RHS typealias F = List<String> // parameterized type at the RHS
typealias H<T> = List<T> // TA with own parameters typealias H<T> = List<T> // TA with own parameters
@@ -21,5 +26,5 @@ expect class T
// Nullability: // Nullability:
typealias U = A // same nullability of the RHS class 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 = 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 typealias Y = V // TA at the RHS with the different nullability of own RHS
@@ -1,9 +1,6 @@
actual class A actual constructor() actual class A actual constructor()
// Lifted up type aliases: // Lifted up type aliases:
typealias D = B // class/TA at the RHS
typealias E = C // different TAs at the RHS
typealias G = List<Int> // different parameterized types at the RHS typealias G = List<Int> // different parameterized types at the RHS
typealias I<T> = List<T> // TAs with own parameters with different names typealias I<T> = List<T> // TAs with own parameters with different names
@@ -1,9 +1,6 @@
actual class A actual constructor() actual class A actual constructor()
// Lifted up type aliases: // Lifted up type aliases:
typealias D = A // class/TA at the RHS
typealias E = B // different TAs at the RHS
typealias G = List<String> // different parameterized types at the RHS typealias G = List<String> // different parameterized types at the RHS
typealias I<R> = List<R> // TAs with own parameters with different names typealias I<R> = List<R> // TAs with own parameters with different names
@@ -2,10 +2,12 @@ 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 = 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 D = B // class/TA expanded to the same class at the RHS
typealias E = C // different TAs at the RHS typealias E = C // different TAs expanded to the same class at the RHS
typealias F = List<String> // parameterized type at the RHS typealias F = List<String> // parameterized type at the RHS
typealias G = List<Int> // different parameterized types at the RHS typealias G = List<Int> // different parameterized types at the RHS
@@ -2,10 +2,12 @@ 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 = 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 D = A // class/TA expanded to the same class at the RHS
typealias E = B // different TAs at the RHS typealias E = B // different TAs expanded to the same class at the RHS
typealias F = List<String> // parameterized type at the RHS typealias F = List<String> // parameterized type at the RHS
typealias G = List<String> // different parameterized types at the RHS typealias G = List<String> // different parameterized types at the RHS
@@ -279,8 +279,13 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Bar") } mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Bar") }
) )
@Test(expected = IllegalCommonizerStateException::class) @Test
fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass1() = doTestFailure( // why success: short-circuiting & lifting up
fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass1() = doTestSuccess(
expected = mockTAType("org.sample.FooAlias") {
mockClassType("org.sample.Foo")
},
mockTAType("org.sample.FooAlias") { mockTAType("org.sample.FooAlias") {
mockClassType("org.sample.Foo") mockClassType("org.sample.Foo")
}, },
@@ -289,13 +294,16 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
mockTAType("org.sample.FooAliasL2") { mockTAType("org.sample.FooAliasL2") {
mockClassType("org.sample.Foo") mockClassType("org.sample.Foo")
} }
}, }
shouldFailOnFirstVariant = true
) )
@Test(expected = IllegalCommonizerStateException::class) @Test
fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass2() = doTestFailure( // 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.FooAlias") {
mockTAType("org.sample.FooAliasL2") { mockTAType("org.sample.FooAliasL2") {
mockClassType("org.sample.Foo") mockClassType("org.sample.Foo")
@@ -304,9 +312,7 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
mockTAType("org.sample.FooAlias") { mockTAType("org.sample.FooAlias") {
mockClassType("org.sample.Foo") mockClassType("org.sample.Foo")
}, }
shouldFailOnFirstVariant = true
) )
@Test @Test