From b13f3599cfdc51f4f8f6d5fb79226f56fe70499f Mon Sep 17 00:00:00 2001 From: "sebastian.sellmair" Date: Sat, 4 Sep 2021 14:11:22 +0200 Subject: [PATCH] [Commonizer] Initial implementation for nullable return type commonization ^KT-48567 --- .../AbstractFunctionOrPropertyCommonizer.kt | 33 ++++++++++++++++--- .../commonizer/core/FunctionCommonizer.kt | 4 +-- .../NullableSingleInvocationCommonizer.kt | 19 ++++++++++- .../commonizer/core/PropertyCommonizer.kt | 4 +-- .../jetbrains/kotlin/commonizer/utils/misc.kt | 18 ++++++++++ .../AbstractInlineSourcesCommonizationTest.kt | 4 +++ 6 files changed, 72 insertions(+), 10 deletions(-) diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/AbstractFunctionOrPropertyCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/AbstractFunctionOrPropertyCommonizer.kt index ce7c307c592..c03027f4d9e 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/AbstractFunctionOrPropertyCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/AbstractFunctionOrPropertyCommonizer.kt @@ -5,21 +5,21 @@ package org.jetbrains.kotlin.commonizer.core -import org.jetbrains.kotlin.commonizer.cir.CirFunctionOrProperty -import org.jetbrains.kotlin.commonizer.cir.CirName +import org.jetbrains.kotlin.commonizer.cir.* 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 abstract class AbstractFunctionOrPropertyCommonizer( classifiers: CirKnownClassifiers -) : AbstractStandardCommonizer() { +) : AbstractStandardCommonizer() { protected lateinit var name: CirName protected val modality = ModalityCommonizer() protected val visibility = VisibilityCommonizer.lowering() protected val extensionReceiver = ExtensionReceiverCommonizer(classifiers) - protected val returnType = TypeCommonizer(classifiers).asCommonizer() + protected val returnType = ReturnTypeCommonizer(classifiers).asCommonizer() protected lateinit var kind: CallableMemberDescriptor.Kind protected val typeParameters = TypeParameterListCommonizer(classifiers) @@ -35,6 +35,29 @@ abstract class AbstractFunctionOrPropertyCommonizer( && modality.commonizeWith(next.modality) && visibility.commonizeWith(next) && extensionReceiver.commonizeWith(next.extensionReceiver) - && returnType.commonizeWith(next.returnType) + && returnType.commonizeWith(next) && typeParameters.commonizeWith(next.typeParameters) } + +private class ReturnTypeCommonizer( + private val classifiers: CirKnownClassifiers, +) : NullableContextualSingleInvocationCommonizer { + override fun invoke(values: List): 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): List { + val simpleTypes = types.safeCastValues() ?: return types + + if ( + simpleTypes.all { type -> type.isMarkedNullable } || + simpleTypes.none { type -> type.isMarkedNullable } + ) return types + + return simpleTypes.map { type -> type.makeNullable() } + } +} + diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/FunctionCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/FunctionCommonizer.kt index 5a0f39cecfc..7db855ae070 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/FunctionCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/FunctionCommonizer.kt @@ -13,7 +13,7 @@ class FunctionCommonizer(classifiers: CirKnownClassifiers) : AbstractFunctionOrP private val modifiers = FunctionModifiersCommonizer() private val valueParameters = CallableValueParametersCommonizer(classifiers) - override fun commonizationResult(): CirFunction { + override fun commonizationResult(): CirFunction? { val valueParameters = valueParameters.result valueParameters.patchCallables() @@ -27,7 +27,7 @@ class FunctionCommonizer(classifiers: CirKnownClassifiers) : AbstractFunctionOrP valueParameters = valueParameters.valueParameters, hasStableParameterNames = valueParameters.hasStableParameterNames, extensionReceiver = extensionReceiver.result, - returnType = returnType.result, + returnType = returnType.result ?: return null, kind = kind, modifiers = modifiers.result ) diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/NullableSingleInvocationCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/NullableSingleInvocationCommonizer.kt index e22a59a4e7b..0e382d06798 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/NullableSingleInvocationCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/NullableSingleInvocationCommonizer.kt @@ -19,4 +19,21 @@ fun NullableSingleInvocationCommonizer.asCommonizer(): Commonizer { + operator fun invoke(values: List): R? +} + +fun NullableContextualSingleInvocationCommonizer.asCommonizer(): Commonizer = object : Commonizer { + private val collectedValues = mutableListOf() + + override val result: R? + get() = this@asCommonizer.invoke(collectedValues) + + override fun commonizeWith(next: T): Boolean { + collectedValues.add(next) + return true + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/PropertyCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/PropertyCommonizer.kt index c429169dbb4..0d21100a6a3 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/PropertyCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/PropertyCommonizer.kt @@ -17,7 +17,7 @@ class PropertyCommonizer(classifiers: CirKnownClassifiers) : AbstractFunctionOrP private var isExternal = true private lateinit var constCommonizationState: ConstCommonizationState - override fun commonizationResult(): CirProperty { + override fun commonizationResult(): CirProperty? { val modality = modality.result val setter = setter.result?.takeIf { setter -> @@ -36,7 +36,7 @@ class PropertyCommonizer(classifiers: CirKnownClassifiers) : AbstractFunctionOrP containingClass = null, // does not matter isExternal = isExternal, extensionReceiver = extensionReceiver.result, - returnType = returnType.result, + returnType = returnType.result ?: return null, kind = kind, isVar = setter != null, isLateInit = false, diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/utils/misc.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/utils/misc.kt index d9b1ef972f1..55b2a7fdff3 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/utils/misc.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/utils/misc.kt @@ -122,3 +122,21 @@ inline fun Array.fastForEach(action: (T) -> Unit) { action(this[index]) } } + +@Suppress("unchecked_cast") +internal inline fun List.safeCastValues(): List? { + fastForEach { if (it !is R) return null } + return this as List +} + + +internal fun List.singleDistinctValueOrNull(): T? = singleDistinctValueOrNull { it } + +internal inline fun List.singleDistinctValueOrNull(selector: (T) -> R): R? { + if (isEmpty()) return null + val value = selector(this[0]) + for (index in 1 until size) { + if (value != selector(this[index])) return null + } + return value +} \ No newline at end of file diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/AbstractInlineSourcesCommonizationTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/AbstractInlineSourcesCommonizationTest.kt index 8a8aace4172..ff6cd062314 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/AbstractInlineSourcesCommonizationTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/AbstractInlineSourcesCommonizationTest.kt @@ -96,6 +96,10 @@ abstract class AbstractInlineSourcesCommonizationTest : KtInlineSourceCommonizer } } + infix fun String.withSource(@Language("kotlin") sourceContent: String) { + simpleSingleSourceTarget(this, sourceContent) + } + @InlineSourcesCommonizationTestDsl fun simpleSingleSourceTarget(target: String, @Language("kotlin") sourceCode: String) { simpleSingleSourceTarget(parseCommonizerTarget(target), sourceCode)