[Commonizer] Split type substitution into forward and backwards

^KT-48288
This commit is contained in:
sebastian.sellmair
2021-09-10 11:47:09 +02:00
committed by Space
parent 698b3fce9e
commit bfa433fa06
6 changed files with 84 additions and 42 deletions
@@ -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<CirTypeProjection>, 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()
}
@@ -92,44 +92,51 @@ internal class ClassOrTypeAliasTypeCommonizer(
}
}
@Suppress("KotlinConstantConditions") // Improved readability
private fun selectClassifierId(types: List<CirClassOrTypeAliasType>): 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<CirTypeProjection>, 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()
}
@@ -18,7 +18,7 @@ class TypeAliasCommonizer(
) : NullableSingleInvocationCommonizer<CirTypeAlias> {
private val typeCommonizer = typeCommonizer.withOptions {
withTypeAliasSubstitutionEnabled(false).withOptimisticNumberTypeCommonizationEnabled(true)
withBackwardsTypeAliasSubstitutionEnabled(false).withOptimisticNumberTypeCommonizationEnabled(true)
}
override fun invoke(values: List<CirTypeAlias>): CirTypeAlias? {
@@ -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 {
@@ -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
@@ -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()
)
}