[Commonizer] Restore TypeAliasCommonizer's Commutative property & TypeAliasTypeCommonizer: substitute underlying-type arguments

^KT-47574 The fix done here supports only one level of substituting
underlying type-arguments.

HierarchicalTypeAliasCommonizationTest.`KT-47574 - test long typealias chain`
will assert proper behaviour for nested chains.
This commit is contained in:
sebastian.sellmair
2021-07-02 16:12:18 +02:00
committed by Space
parent 448302d19b
commit 5b5dddc2d1
8 changed files with 131 additions and 89 deletions
@@ -84,6 +84,8 @@ sealed class CirClassOrTypeAliasType : CirSimpleType() {
if (arguments.isNotEmpty()) arguments.joinTo(builder, prefix = "<", postfix = ">")
super.appendDescriptionTo(builder)
}
abstract fun withArguments(arguments: List<CirTypeProjection>): CirClassOrTypeAliasType
}
abstract class CirClassType : CirClassOrTypeAliasType(), CirHasVisibility {
@@ -98,6 +100,16 @@ abstract class CirClassType : CirClassOrTypeAliasType(), CirHasVisibility {
super.appendDescriptionTo(builder, shortNameOnly = outerType != null)
}
override fun withArguments(arguments: List<CirTypeProjection>): CirClassOrTypeAliasType {
return createInterned(
classId = classifierId,
outerType = outerType,
visibility = visibility,
arguments = arguments,
isMarkedNullable = isMarkedNullable
)
}
companion object {
fun createInterned(
classId: CirEntityId,
@@ -134,6 +146,15 @@ abstract class CirTypeAliasType : CirClassOrTypeAliasType() {
underlyingType.appendDescriptionTo(builder)
}
override fun withArguments(arguments: List<CirTypeProjection>): CirClassOrTypeAliasType {
return createInterned(
typeAliasId = classifierId,
underlyingType = underlyingType,
arguments = arguments,
isMarkedNullable = isMarkedNullable
)
}
companion object {
fun createInterned(
typeAliasId: CirEntityId,
@@ -7,91 +7,29 @@ package org.jetbrains.kotlin.commonizer.core
import org.jetbrains.kotlin.commonizer.cir.*
import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
/**
* 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.
*/
class TypeAliasCommonizer(classifiers: CirKnownClassifiers) : AbstractStandardCommonizer<CirTypeAlias, CirTypeAlias>() {
private val primary = TypeAliasShortCircuitingCommonizer(classifiers)
private val secondary = TypeAliasLiftingUpCommonizer(classifiers)
override fun commonizationResult(): CirTypeAlias = primary.resultOrNull ?: secondary.result
override fun initialize(first: CirTypeAlias) = Unit
override fun doCommonizeWith(next: CirTypeAlias): Boolean {
val primaryResult = primary.commonizeWith(next)
val secondaryResult = secondary.commonizeWith(next)
// Note: don't call commonizeWith() functions in return statement to avoid short-circuiting!
return primaryResult || secondaryResult
}
}
private class TypeAliasShortCircuitingCommonizer(
class TypeAliasCommonizer(
private val classifiers: CirKnownClassifiers
) : AbstractStandardCommonizer<CirTypeAlias, CirTypeAlias>() {
private lateinit var name: CirName
private val typeParameters = TypeParameterListCommonizer(classifiers)
private var underlyingType: CirClassOrTypeAliasType? = null // null means not computed yet
private val expandedType = TypeCommonizer(classifiers).asCommonizer()
private val visibility = VisibilityCommonizer.lowering()
) : AssociativeCommonizer<CirTypeAlias> {
override fun commonizationResult() = CirTypeAlias.create(
annotations = emptyList(),
name = name,
typeParameters = typeParameters.result,
visibility = visibility.result,
underlyingType = underlyingType!!,
expandedType = expandedType.result as CirClassType
)
override fun commonize(first: CirTypeAlias, second: CirTypeAlias): CirTypeAlias? {
val name = if (first.name == second.name) first.name else return null
override fun initialize(first: CirTypeAlias) {
name = first.name
}
val typeParameters = TypeParameterListCommonizer(classifiers)
.commonize(listOf(first.typeParameters, second.typeParameters)) ?: return null
override fun doCommonizeWith(next: CirTypeAlias): Boolean {
if (underlyingType == null) {
underlyingType = computeSuitableUnderlyingType(classifiers, next.underlyingType) ?: return false
}
val underlyingType = TypeCommonizer(classifiers)
.commonize(first.underlyingType, second.underlyingType) as? CirClassOrTypeAliasType ?: return null
return typeParameters.commonizeWith(next.typeParameters)
&& expandedType.commonizeWith(next.expandedType)
&& visibility.commonizeWith(next)
}
}
private class TypeAliasLiftingUpCommonizer(classifiers: CirKnownClassifiers) : AbstractStandardCommonizer<CirTypeAlias, CirTypeAlias>() {
private lateinit var name: CirName
private val typeParameters = TypeParameterListCommonizer(classifiers)
private val underlyingType = TypeCommonizer(classifiers).asCommonizer()
private val visibility = VisibilityCommonizer.lowering()
override fun commonizationResult(): CirTypeAlias {
val underlyingType = underlyingType.result as CirClassOrTypeAliasType
val visibility = VisibilityCommonizer.lowering().commonize(listOf(first, second)) ?: return null
return CirTypeAlias.create(
annotations = emptyList(),
name = name,
typeParameters = typeParameters.result,
visibility = visibility.result,
typeParameters = typeParameters,
visibility = visibility,
underlyingType = underlyingType,
expandedType = computeExpandedType(underlyingType)
expandedType = underlyingType.expandedType()
)
}
override fun initialize(first: CirTypeAlias) {
name = first.name
}
override fun doCommonizeWith(next: CirTypeAlias) =
typeParameters.commonizeWith(next.typeParameters)
&& underlyingType.commonizeWith(next.underlyingType)
&& visibility.commonizeWith(next)
}
}
@@ -15,6 +15,7 @@ internal class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifi
private lateinit var typeAliasId: CirEntityId
private val arguments = TypeArgumentListCommonizer(classifiers)
private val underlyingTypeArguments = TypeArgumentListCommonizer(classifiers)
private var isMarkedNullable = false
private var commonizedTypeBuilder: CommonizedTypeAliasTypeBuilder? = null // null means not selected yet
@@ -22,6 +23,7 @@ internal class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifi
(commonizedTypeBuilder ?: failInEmptyState()).build(
typeAliasId = typeAliasId,
arguments = arguments.result,
underlyingTypeArguments = underlyingTypeArguments.result,
isMarkedNullable = isMarkedNullable
)
@@ -50,24 +52,34 @@ internal class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifi
}
}
return arguments.commonizeWith(next.arguments)
return arguments.commonizeWith(next.arguments) &&
underlyingTypeArguments.commonizeWith(next.underlyingType.arguments)
}
// builds a new type for "common" library fragment for the given combination of type alias types in "platform" fragments
internal interface CommonizedTypeAliasTypeBuilder {
fun build(typeAliasId: CirEntityId, arguments: List<CirTypeProjection>, isMarkedNullable: Boolean): CirClassOrTypeAliasType
fun build(
typeAliasId: CirEntityId,
arguments: List<CirTypeProjection>,
underlyingTypeArguments: List<CirTypeProjection>,
isMarkedNullable: Boolean
): CirClassOrTypeAliasType
companion object {
// type alias has been commonized to expect class, need to build type for expect class
fun forClass(commonClass: CirClass) = object : CommonizedTypeAliasTypeBuilder {
override fun build(typeAliasId: CirEntityId, arguments: List<CirTypeProjection>, isMarkedNullable: Boolean) =
CirClassType.createInterned(
classId = typeAliasId,
outerType = null, // there can't be outer type
visibility = commonClass.visibility,
arguments = arguments,
isMarkedNullable = isMarkedNullable
)
override fun build(
typeAliasId: CirEntityId,
arguments: List<CirTypeProjection>,
underlyingTypeArguments: List<CirTypeProjection>,
isMarkedNullable: Boolean
) = CirClassType.createInterned(
classId = typeAliasId,
outerType = null, // there can't be outer type
visibility = commonClass.visibility,
arguments = arguments,
isMarkedNullable = isMarkedNullable
)
}
// type alias has been commonized to another type alias with the different underlying type, need to build type for
@@ -79,12 +91,16 @@ internal class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifi
override fun build(
typeAliasId: CirEntityId,
arguments: List<CirTypeProjection>,
underlyingTypeArguments: List<CirTypeProjection>,
isMarkedNullable: Boolean
): CirTypeAliasType {
val underlyingTypeWithProperNullability = underlyingType.makeNullableIfNecessary(isMarkedNullable)
val underlyingTypeWithProperNullability = underlyingType
.makeNullableIfNecessary(isMarkedNullable)
.withArguments(underlyingTypeArguments)
return CirTypeAliasType.createInterned(
typeAliasId = typeAliasId,
underlyingType = underlyingTypeWithProperNullability, // TODO replace arguments???
underlyingType = underlyingTypeWithProperNullability,
arguments = arguments,
isMarkedNullable = isMarkedNullable
)
@@ -115,7 +115,7 @@ internal fun buildTypeAliasNode(
storageManager = storageManager,
size = size,
nodeRelationship = null,
commonizerProducer = { TypeAliasCommonizer(classifiers) },
commonizerProducer = { TypeAliasCommonizer(classifiers).asCommonizer() },
recursionMarker = CirTypeAliasRecursionMarker,
nodeProducer = { targetDeclarations, commonDeclaration ->
CirTypeAliasNode(typeAliasId, targetDeclarations, commonDeclaration).also {
@@ -6,7 +6,9 @@ typealias C2 = C // TA lifted up as is
typealias C3 = C2 // TA lifted up as is
typealias D = A // class/TA expanded to the same class at the RHS
typealias E = B // different TAs use common type from TA-chain
typealias D2 = A // class/TA expanded to the same class at the RHS
typealias E = A // different TAs use common type from TA-chain
typealias E2 = A // different TAs use common type from TA-chain
typealias F = List<String> // parameterized type at the RHS
typealias H<T> = List<T> // TA with own parameters
@@ -7,7 +7,9 @@ typealias C2 = C // TA lifted up as is
typealias C3 = C2 // TA lifted up as is
typealias D = B // class/TA expanded to the same class at the RHS
typealias D2 = A //class/TA expanded to the same class at RHS
typealias E = C // different TAs use common type from TA-chain
typealias E2 = B
typealias F = List<String> // parameterized type at the RHS
typealias G = List<Int> // different parameterized types at the RHS
@@ -7,7 +7,9 @@ typealias C2 = C // TA lifted up as is
typealias C3 = C2 // TA lifted up as is
typealias D = A // class/TA expanded to the same class at the RHS
typealias D2 = B
typealias E = B // different TAs use common type from TA-chain
typealias E2 = C
typealias F = List<String> // parameterized type at the RHS
typealias G = List<String> // different parameterized types at the RHS
@@ -101,4 +101,65 @@ class HierarchicalTypeAliasCommonizationTest : AbstractInlineSourcesCommonizatio
result.assertCommonized("(a, b, c, d)", """expect class x expect constructor()""")
result.assertCommonized("(a, b, c, d, e, f)", """expect class x expect constructor()""")
}
fun `test typealias chain`() {
val result = commonize {
outputTarget("(a, b)")
simpleSingleSourceTarget(
"a", """
typealias A<N, M> = Map<N, M>
typealias B = A<String, Int>
""".trimIndent()
)
simpleSingleSourceTarget(
"b", """
typealias A<N, M> = Map<N, M>
typealias B = A<String, Int>
""".trimIndent()
)
}
result.assertCommonized(
"(a, b)", """
typealias A<N, M> = Map<N, M>
typealias B = A<String, Int>
""".trimIndent()
)
}
fun `KT-47574 - test long typealias chain`() {
val result = commonize {
outputTarget("(a, b)")
simpleSingleSourceTarget(
"a", """
typealias A<N, M> = Map<N, M>
typealias B<N, M> = A<N, M>
typealias C<N> = B<N, Int>
typealias D = C<String>
""".trimIndent()
)
simpleSingleSourceTarget(
"b", """
typealias A<N, M> = Map<N, M>
typealias B<N, M> = A<N, M>
typealias C<N> = B<N, Int>
typealias D = C<String>
""".trimIndent()
)
}
result.assertCommonized(
"(a, b)", """
typealias A<N, M> = Map<N, M>
typealias B<N, M> = A<N, M>
typealias C<N> = B<N, Int>
typealias D = C<String>
""".trimIndent()
)
}
}