[Commonizer] Implement TypeNullabilityCommonizer and ReturnTypeCommonizer
^KT-48567 Verification Pending
This commit is contained in:
committed by
Space
parent
b13f3599cf
commit
9e34382db5
+6
-16
@@ -5,9 +5,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.core
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.*
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirFunctionOrProperty
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirName
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirType
|
||||
import org.jetbrains.kotlin.commonizer.core.TypeCommonizer.Options.Companion.default
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers
|
||||
import org.jetbrains.kotlin.commonizer.utils.safeCastValues
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DELEGATION
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED
|
||||
@@ -45,19 +47,7 @@ private class ReturnTypeCommonizer(
|
||||
override fun invoke(values: List<CirFunctionOrProperty>): CirType? {
|
||||
if (values.isEmpty()) return null
|
||||
val isTopLevel = values.all { it.containingClass == null }
|
||||
val returnTypes = if (isTopLevel) makeNullableIfNecessary(values.map { it.returnType }) else values.map { it.returnType }
|
||||
return TypeCommonizer(classifiers).asCommonizer().commonize(returnTypes)
|
||||
}
|
||||
|
||||
private fun makeNullableIfNecessary(types: List<CirType>): List<CirType> {
|
||||
val simpleTypes = types.safeCastValues<CirType, CirSimpleType>() ?: return types
|
||||
|
||||
if (
|
||||
simpleTypes.all { type -> type.isMarkedNullable } ||
|
||||
simpleTypes.none { type -> type.isMarkedNullable }
|
||||
) return types
|
||||
|
||||
return simpleTypes.map { type -> type.makeNullable() }
|
||||
return TypeCommonizer(classifiers, default.withCovariantNullabilityCommonizationEnabled(isTopLevel))
|
||||
.asCommonizer().commonize(values.map { it.returnType })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-6
@@ -17,8 +17,8 @@ internal class ClassOrTypeAliasTypeCommonizer(
|
||||
|
||||
override fun commonize(first: CirClassOrTypeAliasType, second: CirClassOrTypeAliasType): CirClassOrTypeAliasType? {
|
||||
if (first is CirClassType && second is CirClassType) {
|
||||
return ClassTypeCommonizer(classifiers).commonize(listOf(first, second))
|
||||
?: if (options.allowOptimisticNumberTypeCommonization) OptimisticNumbersTypeCommonizer.commonize(first, second) else null
|
||||
return ClassTypeCommonizer(classifiers, options).commonize(listOf(first, second))
|
||||
?: if (options.enableOptimisticNumberTypeCommonization) OptimisticNumbersTypeCommonizer.commonize(first, second) else null
|
||||
}
|
||||
|
||||
if (first is CirTypeAliasType && second is CirTypeAliasType) {
|
||||
@@ -26,9 +26,10 @@ internal class ClassOrTypeAliasTypeCommonizer(
|
||||
In case regular type-alias-type commonization fails, we try to expand all type-aliases and
|
||||
try our luck with commonizing those class types
|
||||
*/
|
||||
return TypeAliasTypeCommonizer(classifiers).commonize(listOf(first, second))
|
||||
?: ClassOrTypeAliasTypeCommonizer(classifiers, options.withAllowOptimisticNumberTypeCommonization())
|
||||
.commonize(first.expandedType(), second.expandedType())
|
||||
return TypeAliasTypeCommonizer(classifiers, options).commonize(listOf(first, second))
|
||||
?: ClassOrTypeAliasTypeCommonizer(
|
||||
classifiers, options.withOptimisticNumberTypeCommonizationEnabled()
|
||||
).commonize(first.expandedType(), second.expandedType())
|
||||
}
|
||||
|
||||
val classType = when {
|
||||
@@ -47,7 +48,7 @@ internal class ClassOrTypeAliasTypeCommonizer(
|
||||
TypeAliasCommonizer will be able to figure out if the typealias will be represented as expect class in common.
|
||||
If so, re-use this class type, otherwise: try to expand the typeAlias
|
||||
*/
|
||||
val typeAliasClassType = TypeAliasTypeCommonizer(classifiers).commonize(listOf(typeAliasType))?.expandedType()
|
||||
val typeAliasClassType = TypeAliasTypeCommonizer(classifiers, options).commonize(listOf(typeAliasType))?.expandedType()
|
||||
?: typeAliasType.expandedType()
|
||||
|
||||
return commonize(classType, typeAliasClassType)
|
||||
|
||||
@@ -11,13 +11,16 @@ import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers
|
||||
import org.jetbrains.kotlin.commonizer.utils.isUnderKotlinNativeSyntheticPackages
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
|
||||
internal class ClassTypeCommonizer(private val classifiers: CirKnownClassifiers) :
|
||||
internal class ClassTypeCommonizer(
|
||||
private val classifiers: CirKnownClassifiers,
|
||||
options: TypeCommonizer.Options = TypeCommonizer.Options.default
|
||||
) :
|
||||
AbstractStandardCommonizer<CirClassType, CirClassType>() {
|
||||
private lateinit var classId: CirEntityId
|
||||
private val outerType = OuterClassTypeCommonizer(classifiers)
|
||||
private lateinit var anyVisibility: Visibility
|
||||
private val arguments = TypeArgumentListCommonizer(classifiers)
|
||||
private var isMarkedNullable = false
|
||||
private val isMarkedNullable = TypeNullabilityCommonizer(options).asCommonizer()
|
||||
|
||||
override fun commonizationResult() = CirClassType.createInterned(
|
||||
classId = classId,
|
||||
@@ -28,17 +31,16 @@ internal class ClassTypeCommonizer(private val classifiers: CirKnownClassifiers)
|
||||
// to reach better interning rate.
|
||||
visibility = anyVisibility,
|
||||
arguments = arguments.result,
|
||||
isMarkedNullable = isMarkedNullable
|
||||
isMarkedNullable = isMarkedNullable.result
|
||||
)
|
||||
|
||||
override fun initialize(first: CirClassType) {
|
||||
classId = first.classifierId
|
||||
anyVisibility = first.visibility
|
||||
isMarkedNullable = first.isMarkedNullable
|
||||
}
|
||||
|
||||
override fun doCommonizeWith(next: CirClassType) =
|
||||
isMarkedNullable == next.isMarkedNullable
|
||||
isMarkedNullable.commonizeWith(next.isMarkedNullable)
|
||||
&& classId == next.classifierId
|
||||
&& outerType.commonizeWith(next.outerType)
|
||||
&& isClassifierAvailableInCommon(classifiers, classId)
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.core
|
||||
|
||||
interface NullableContextualSingleInvocationCommonizer<T : Any, R : Any> {
|
||||
operator fun invoke(values: List<T>): R?
|
||||
}
|
||||
|
||||
fun <Context : Any, R : Any> NullableContextualSingleInvocationCommonizer<Context, R>.asCommonizer(): Commonizer<Context, R?> =
|
||||
object : Commonizer<Context, R?> {
|
||||
private val collectedValues = mutableListOf<Context>()
|
||||
|
||||
override val result: R?
|
||||
get() = this@asCommonizer.invoke(collectedValues)
|
||||
|
||||
override fun commonizeWith(next: Context): Boolean {
|
||||
collectedValues.add(next)
|
||||
return true
|
||||
}
|
||||
}
|
||||
-15
@@ -22,18 +22,3 @@ fun <T : Any> NullableSingleInvocationCommonizer<T>.asCommonizer(): Commonizer<T
|
||||
}
|
||||
|
||||
|
||||
interface NullableContextualSingleInvocationCommonizer<T : Any, R : Any> {
|
||||
operator fun invoke(values: List<T>): R?
|
||||
}
|
||||
|
||||
fun <T : Any, R: Any> NullableContextualSingleInvocationCommonizer<T, R>.asCommonizer(): Commonizer<T, R?> = object : Commonizer<T, R?> {
|
||||
private val collectedValues = mutableListOf<T>()
|
||||
|
||||
override val result: R?
|
||||
get() = this@asCommonizer.invoke(collectedValues)
|
||||
|
||||
override fun commonizeWith(next: T): Boolean {
|
||||
collectedValues.add(next)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ class TypeAliasCommonizer(
|
||||
|
||||
val typeParameters = TypeParameterListCommonizer(classifiers).commonize(values.map { it.typeParameters }) ?: return null
|
||||
|
||||
val underlyingType = TypeCommonizer(classifiers, TypeCommonizer.Options.default.withAllowOptimisticNumberTypeCommonization())
|
||||
val underlyingType = TypeCommonizer(classifiers, TypeCommonizer.Options.default.withOptimisticNumberTypeCommonizationEnabled())
|
||||
.asCommonizer().commonize(values.map { it.underlyingType }) as? CirClassOrTypeAliasType ?: return null
|
||||
|
||||
val visibility = VisibilityCommonizer.lowering().commonize(values) ?: return null
|
||||
|
||||
+8
-6
@@ -10,13 +10,16 @@ import org.jetbrains.kotlin.commonizer.core.CommonizedTypeAliasAnswer.Companion.
|
||||
import org.jetbrains.kotlin.commonizer.core.CommonizedTypeAliasAnswer.Companion.SUCCESS_FROM_DEPENDENCY_LIBRARY
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers
|
||||
|
||||
internal class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifiers) :
|
||||
internal class TypeAliasTypeCommonizer(
|
||||
private val classifiers: CirKnownClassifiers,
|
||||
options: TypeCommonizer.Options
|
||||
) :
|
||||
AbstractStandardCommonizer<CirTypeAliasType, CirClassOrTypeAliasType>() {
|
||||
|
||||
private lateinit var typeAliasId: CirEntityId
|
||||
private val arguments = TypeArgumentListCommonizer(classifiers)
|
||||
private val underlyingTypeArguments = TypeArgumentListCommonizer(classifiers)
|
||||
private var isMarkedNullable = false
|
||||
private val isMarkedNullable = TypeNullabilityCommonizer(options).asCommonizer()
|
||||
private var commonizedTypeBuilder: CommonizedTypeAliasTypeBuilder? = null // null means not selected yet
|
||||
|
||||
override fun commonizationResult() =
|
||||
@@ -24,17 +27,16 @@ internal class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifi
|
||||
typeAliasId = typeAliasId,
|
||||
arguments = arguments.result,
|
||||
underlyingTypeArguments = underlyingTypeArguments.result,
|
||||
isMarkedNullable = isMarkedNullable
|
||||
isMarkedNullable = isMarkedNullable.result
|
||||
)
|
||||
|
||||
override fun initialize(first: CirTypeAliasType) {
|
||||
typeAliasId = first.classifierId
|
||||
isMarkedNullable = first.expandedType().isMarkedNullable
|
||||
}
|
||||
|
||||
override fun doCommonizeWith(next: CirTypeAliasType): Boolean {
|
||||
if (isMarkedNullable != next.expandedType().isMarkedNullable || typeAliasId != next.classifierId)
|
||||
return false
|
||||
if (typeAliasId != next.classifierId) return false
|
||||
if (!isMarkedNullable.commonizeWith(next.expandedType().isMarkedNullable)) return false
|
||||
|
||||
if (commonizedTypeBuilder == null) {
|
||||
val answer = commonizeTypeAlias(typeAliasId, classifiers)
|
||||
|
||||
@@ -30,12 +30,18 @@ class TypeCommonizer(
|
||||
}
|
||||
|
||||
data class Options(
|
||||
val allowOptimisticNumberTypeCommonization: Boolean = false
|
||||
val enableOptimisticNumberTypeCommonization: Boolean = false,
|
||||
val enableCovariantNullabilityCommonization: Boolean = false
|
||||
) {
|
||||
|
||||
fun withAllowOptimisticNumberTypeCommonization(): Options {
|
||||
return if (allowOptimisticNumberTypeCommonization) this
|
||||
else copy(allowOptimisticNumberTypeCommonization = true)
|
||||
fun withOptimisticNumberTypeCommonizationEnabled(enabled: Boolean = true): Options {
|
||||
return if (enableOptimisticNumberTypeCommonization == enabled) this
|
||||
else copy(enableOptimisticNumberTypeCommonization = enabled)
|
||||
}
|
||||
|
||||
fun withCovariantNullabilityCommonizationEnabled(enabled: Boolean = true): Options {
|
||||
return if (enableCovariantNullabilityCommonization == enabled) this
|
||||
else copy(enableCovariantNullabilityCommonization = enabled)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.core
|
||||
|
||||
private typealias IsMarkedNullable = Boolean
|
||||
|
||||
internal interface TypeNullabilityCommonizer : AssociativeCommonizer<IsMarkedNullable>
|
||||
|
||||
internal fun TypeNullabilityCommonizer(options: TypeCommonizer.Options): TypeNullabilityCommonizer {
|
||||
return if (options.enableCovariantNullabilityCommonization) CovariantTypeNullabilityCommonizer
|
||||
else EqualTypeNullabilityCommonizer
|
||||
}
|
||||
|
||||
private object CovariantTypeNullabilityCommonizer : TypeNullabilityCommonizer {
|
||||
override fun commonize(first: IsMarkedNullable, second: IsMarkedNullable): IsMarkedNullable {
|
||||
return first || second
|
||||
}
|
||||
}
|
||||
|
||||
private object EqualTypeNullabilityCommonizer : TypeNullabilityCommonizer {
|
||||
override fun commonize(first: IsMarkedNullable, second: IsMarkedNullable): IsMarkedNullable? {
|
||||
if (first != second) return null
|
||||
return first
|
||||
}
|
||||
}
|
||||
+12
-3
@@ -92,7 +92,7 @@ class ReturnTypeNullabilityCommonizationTest : AbstractInlineSourcesCommonizatio
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
expect class X
|
||||
expect fun x(): X
|
||||
expect fun x(): X?
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
@@ -124,7 +124,9 @@ class ReturnTypeNullabilityCommonizationTest : AbstractInlineSourcesCommonizatio
|
||||
typealias Z = Any
|
||||
expect class Y
|
||||
expect class X
|
||||
fun x(): X
|
||||
|
||||
// Still cary nullability mark here to be extra safe
|
||||
expect fun x(): X?
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
@@ -156,7 +158,8 @@ class ReturnTypeNullabilityCommonizationTest : AbstractInlineSourcesCommonizatio
|
||||
typealias Z = Any
|
||||
expect class Y
|
||||
expect class X
|
||||
expect val x: X
|
||||
// Still cary nullability mark here to be more safe!
|
||||
expect val x: X?
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
@@ -176,6 +179,12 @@ class ReturnTypeNullabilityCommonizationTest : AbstractInlineSourcesCommonizatio
|
||||
result.assertCommonized("(a, b, c, d)", "expect val x: Any?")
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
We expect *no* covariant nullability commonization on any member function/property because this might mess
|
||||
with overrides of super classes/interfaces
|
||||
*/
|
||||
|
||||
fun `test member - property - hierarchically`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)", "(c, d)", "(a, b, c, d)")
|
||||
|
||||
Reference in New Issue
Block a user