[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
)
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:
@@ -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")
@@ -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
)
}
@@ -18,7 +18,7 @@ data class CirTypeAliasImpl(
override val typeParameters: List<CirTypeParameter>,
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
@@ -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<CirTypeAlias, CirClassifier>() {
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<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 lateinit var name: Name
private val typeParameters = TypeParameterListCommonizer(cache)
@@ -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<String> // parameterized type at the RHS
typealias H<T> = List<T> // 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
@@ -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<Int> // different parameterized types at the RHS
typealias I<T> = List<T> // TAs with own parameters with different names
@@ -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<String> // different parameterized types at the RHS
typealias I<R> = List<R> // TAs with own parameters with different names
@@ -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<String> // parameterized type at the RHS
typealias G = List<Int> // different parameterized types at the RHS
@@ -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<String> // parameterized type 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") }
)
@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<CirType, CirType>() {
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<CirType, CirType>() {
mockTAType("org.sample.FooAlias") {
mockClassType("org.sample.Foo")
},
shouldFailOnFirstVariant = true
}
)
@Test