[MPP] Apply @UnsafeNumber to functions and properties
KT-51224 KT-51215
This commit is contained in:
committed by
teamcity
parent
f8a1395a03
commit
6674c3f482
+1
-1
@@ -120,7 +120,7 @@ rootProject.apply {
|
||||
IdeVersionConfigurator.setCurrentIde(project)
|
||||
|
||||
if (!project.hasProperty("versions.kotlin-native")) {
|
||||
extra["versions.kotlin-native"] = "1.7.0-dev-1132"
|
||||
extra["versions.kotlin-native"] = "1.7.0-dev-1827"
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
package kotlinx.cinterop
|
||||
|
||||
/**
|
||||
* Marker for typealias that will represent numbers of different bit width on at least two platforms,
|
||||
* or function/property that have such numbers in their signature.
|
||||
* Marker for declarations that depend on numeric types of different bit width on at least two platforms.
|
||||
*
|
||||
* @param actualPlatformTypes: Contains platform types represented as `{konanTarget}: {type fqn}`
|
||||
* e.g. ["linux_x64: kotlin.Int", "linux_arm64: kotlin.Long"]
|
||||
|
||||
+24
-2
@@ -5,15 +5,20 @@
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.core
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.CommonizerSettings
|
||||
import org.jetbrains.kotlin.commonizer.cir.*
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers
|
||||
import org.jetbrains.kotlin.commonizer.utils.singleDistinctValueOrNull
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DELEGATION
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
class FunctionOrPropertyBaseCommonizer(
|
||||
private val classifiers: CirKnownClassifiers,
|
||||
private val settings: CommonizerSettings,
|
||||
private val typeCommonizer: TypeCommonizer,
|
||||
private val extensionReceiverCommonizer: ExtensionReceiverCommonizer = ExtensionReceiverCommonizer(typeCommonizer),
|
||||
private val returnTypeCommonizer: ReturnTypeCommonizer = ReturnTypeCommonizer(typeCommonizer),
|
||||
@@ -26,7 +31,8 @@ class FunctionOrPropertyBaseCommonizer(
|
||||
val visibility: Visibility,
|
||||
val extensionReceiver: CirExtensionReceiver?,
|
||||
val returnType: CirType,
|
||||
val typeParameters: List<CirTypeParameter>
|
||||
val typeParameters: List<CirTypeParameter>,
|
||||
val additionalAnnotations: List<CirAnnotation>,
|
||||
)
|
||||
|
||||
override fun invoke(values: List<CirFunctionOrProperty>): FunctionOrProperty? {
|
||||
@@ -43,6 +49,12 @@ class FunctionOrPropertyBaseCommonizer(
|
||||
return null
|
||||
}
|
||||
|
||||
val additionalUnsafeNumberAnnotation =
|
||||
createUnsafeNumberAnnotationIfNecessary(
|
||||
classifiers.classifierIndices.targets, settings, values,
|
||||
getTypeIdFromDeclarationForCheck = ::getFunctionOrPropertyReturnTypeId,
|
||||
)
|
||||
|
||||
return FunctionOrProperty(
|
||||
name = values.first().name,
|
||||
kind = values.singleDistinctValueOrNull { it.kind } ?: return null,
|
||||
@@ -50,8 +62,18 @@ class FunctionOrPropertyBaseCommonizer(
|
||||
visibility = VisibilityCommonizer.lowering().commonize(values) ?: return null,
|
||||
extensionReceiver = (extensionReceiverCommonizer(values.map { it.extensionReceiver }) ?: return null).receiver,
|
||||
returnType = returnTypeCommonizer(values) ?: return null,
|
||||
typeParameters = TypeParameterListCommonizer(typeCommonizer).commonize(values.map { it.typeParameters }) ?: return null
|
||||
typeParameters = TypeParameterListCommonizer(typeCommonizer).commonize(values.map { it.typeParameters }) ?: return null,
|
||||
additionalAnnotations = listOfNotNull(additionalUnsafeNumberAnnotation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFunctionOrPropertyReturnTypeId(functionOrProperty: CirFunctionOrProperty): CirEntityId? {
|
||||
return functionOrProperty.returnType.let { returnType ->
|
||||
when (returnType) {
|
||||
is CirFlexibleType -> returnType.lowerBound.safeAs<CirClassOrTypeAliasType>()?.classifierId
|
||||
else -> returnType.safeAs<CirClassOrTypeAliasType>()?.classifierId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@ class FunctionCommonizer(
|
||||
val functionOrProperty = functionOrPropertyBaseCommonizer(values) ?: return null
|
||||
val valueParametersResult = CallableValueParametersCommonizer(typeCommonizer).commonize(values) ?: return null
|
||||
return CirFunction(
|
||||
annotations = AnnotationsCommonizer().commonize(values.map { it.annotations }) ?: return null,
|
||||
annotations = AnnotationsCommonizer().commonize(values.map { it.annotations })
|
||||
?.plus(functionOrProperty.additionalAnnotations)
|
||||
?: return null,
|
||||
name = values.first().name,
|
||||
typeParameters = functionOrProperty.typeParameters,
|
||||
visibility = functionOrProperty.visibility,
|
||||
|
||||
@@ -30,7 +30,7 @@ class PropertyCommonizer(
|
||||
val constCompileTimeInitializer = (constCommonizationState as? ConstSameValue)?.compileTimeInitializer
|
||||
|
||||
return CirProperty(
|
||||
annotations = emptyList(),
|
||||
annotations = functionOrPropertyBase.additionalAnnotations,
|
||||
name = functionOrPropertyBase.name,
|
||||
typeParameters = functionOrPropertyBase.typeParameters,
|
||||
visibility = functionOrPropertyBase.visibility,
|
||||
|
||||
@@ -6,12 +6,8 @@
|
||||
package org.jetbrains.kotlin.commonizer.core
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.CommonizerSettings
|
||||
import org.jetbrains.kotlin.commonizer.CommonizerTarget
|
||||
import org.jetbrains.kotlin.commonizer.OptimisticNumberCommonizationEnabledKey
|
||||
import org.jetbrains.kotlin.commonizer.allLeaves
|
||||
import org.jetbrains.kotlin.commonizer.cir.*
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
|
||||
class TypeAliasCommonizer(
|
||||
typeCommonizer: TypeCommonizer,
|
||||
@@ -41,67 +37,11 @@ class TypeAliasCommonizer(
|
||||
underlyingType = underlyingType,
|
||||
expandedType = underlyingType.expandedType(),
|
||||
annotations = listOfNotNull(
|
||||
createUnsafeNumberAnnotationIfNecessary(classifiers.classifierIndices.targets, settings, values)
|
||||
createUnsafeNumberAnnotationIfNecessary(
|
||||
classifiers.classifierIndices.targets, settings, values,
|
||||
getTypeIdFromDeclarationForCheck = { typeAlias -> typeAlias.expandedType.classifierId }
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createUnsafeNumberAnnotationIfNecessary(
|
||||
targets: List<CommonizerTarget>,
|
||||
settings: CommonizerSettings,
|
||||
values: List<CirTypeAlias>,
|
||||
): CirAnnotation? {
|
||||
val isOptimisticCommonizationEnabled = settings.getSetting(OptimisticNumberCommonizationEnabledKey)
|
||||
|
||||
if (!isOptimisticCommonizationEnabled)
|
||||
return null
|
||||
|
||||
val expandedTypes = values.map { it.expandedType.classifierId }
|
||||
|
||||
// All typealias have to be potentially substitutable (aka have to be some kind of number type)
|
||||
if (!expandedTypes.all { OptimisticNumbersTypeCommonizer.isOptimisticallySubstitutable(it) }) {
|
||||
return null
|
||||
}
|
||||
|
||||
val actualPlatformTypes = mutableMapOf<String, CirEntityId>()
|
||||
values.forEachIndexed forEach@{ index, ta ->
|
||||
val existingAnnotation = ta.annotations.firstIsInstanceOrNull<UnsafeNumberAnnotation>()
|
||||
if (existingAnnotation != null) {
|
||||
actualPlatformTypes.putAll(existingAnnotation.actualPlatformTypes)
|
||||
return@forEach
|
||||
}
|
||||
|
||||
targets[index].allLeaves().forEach { target ->
|
||||
actualPlatformTypes[target.name] = ta.expandedType.classifierId
|
||||
}
|
||||
}
|
||||
|
||||
if (actualPlatformTypes.values.distinct().size > 1) {
|
||||
return UnsafeNumberAnnotation(actualPlatformTypes)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private class UnsafeNumberAnnotation(val actualPlatformTypes: Map<String, CirEntityId>) : CirAnnotation {
|
||||
override val type: CirClassType = UnsafeNumberAnnotation.type
|
||||
override val annotationValueArguments: Map<CirName, CirAnnotation> = emptyMap()
|
||||
|
||||
override val constantValueArguments: Map<CirName, CirConstantValue> = mapOf(
|
||||
CirName.create("actualPlatformTypes") to CirConstantValue.ArrayValue(
|
||||
actualPlatformTypes.map { (platform, type) -> CirConstantValue.StringValue("$platform: ${type.toQualifiedNameString()}") }
|
||||
)
|
||||
)
|
||||
|
||||
companion object {
|
||||
private val type = CirClassType.createInterned(
|
||||
classId = CirEntityId.create("kotlinx/cinterop/UnsafeNumber"),
|
||||
outerType = null,
|
||||
arguments = emptyList(),
|
||||
isMarkedNullable = false
|
||||
)
|
||||
|
||||
val empty = UnsafeNumberAnnotation(emptyMap())
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.CommonizerSettings
|
||||
import org.jetbrains.kotlin.commonizer.CommonizerTarget
|
||||
import org.jetbrains.kotlin.commonizer.OptimisticNumberCommonizationEnabledKey
|
||||
import org.jetbrains.kotlin.commonizer.allLeaves
|
||||
import org.jetbrains.kotlin.commonizer.cir.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
|
||||
internal fun <T : CirHasAnnotations> createUnsafeNumberAnnotationIfNecessary(
|
||||
targets: List<CommonizerTarget>,
|
||||
settings: CommonizerSettings,
|
||||
values: List<T>,
|
||||
getTypeIdFromDeclarationForCheck: (declaration: T) -> CirEntityId?,
|
||||
): CirAnnotation? {
|
||||
val isOptimisticCommonizationEnabled = settings.getSetting(OptimisticNumberCommonizationEnabledKey)
|
||||
|
||||
if (!isOptimisticCommonizationEnabled)
|
||||
return null
|
||||
|
||||
val typeIds = values.map { annotated -> getTypeIdFromDeclarationForCheck(annotated) }
|
||||
|
||||
// All typealias have to be potentially substitutable (aka have to be some kind of number type)
|
||||
if (!typeIds.all { it != null && OptimisticNumbersTypeCommonizer.isOptimisticallySubstitutable(it) }) {
|
||||
return null
|
||||
}
|
||||
|
||||
return createUnsafeNumberAnnotation(targets, values, getTypeIdFromDeclarationForCheck)
|
||||
}
|
||||
|
||||
private fun <T : CirHasAnnotations> createUnsafeNumberAnnotation(
|
||||
targets: List<CommonizerTarget>,
|
||||
values: List<T>,
|
||||
getTypeIdFromDeclaration: (declaration: T) -> CirEntityId?,
|
||||
): CirAnnotation? {
|
||||
val actualPlatformTypes = mutableMapOf<String, CirEntityId>()
|
||||
|
||||
values.forEachIndexed forEach@{ index, annotated ->
|
||||
val existingAnnotation = annotated.annotations.firstIsInstanceOrNull<UnsafeNumberAnnotation>()
|
||||
if (existingAnnotation != null) {
|
||||
actualPlatformTypes.putAll(existingAnnotation.actualPlatformTypes)
|
||||
return@forEach
|
||||
}
|
||||
|
||||
targets[index].allLeaves().forEach { target ->
|
||||
actualPlatformTypes[target.name] = getTypeIdFromDeclaration(annotated)
|
||||
?: throw IllegalStateException("Expect class or type alias type")
|
||||
}
|
||||
}
|
||||
|
||||
if (actualPlatformTypes.values.distinct().size > 1) {
|
||||
return UnsafeNumberAnnotation(actualPlatformTypes)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private class UnsafeNumberAnnotation(val actualPlatformTypes: Map<String, CirEntityId>) : CirAnnotation {
|
||||
override val type: CirClassType = UnsafeNumberAnnotation.type
|
||||
override val annotationValueArguments: Map<CirName, CirAnnotation> = emptyMap()
|
||||
|
||||
override val constantValueArguments: Map<CirName, CirConstantValue> = mapOf(
|
||||
CirName.create("actualPlatformTypes") to CirConstantValue.ArrayValue(
|
||||
actualPlatformTypes.toSortedMap().map { (platform, type) ->
|
||||
CirConstantValue.StringValue("$platform: ${type.toQualifiedNameString()}")
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
companion object {
|
||||
private val type = CirClassType.createInterned(
|
||||
classId = CirEntityId.create("kotlinx/cinterop/UnsafeNumber"),
|
||||
outerType = null,
|
||||
arguments = emptyList(),
|
||||
isMarkedNullable = false
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,9 @@ internal fun buildPropertyNode(
|
||||
storageManager = storageManager,
|
||||
size = size,
|
||||
nodeRelationship = nodeRelationship,
|
||||
commonizerProducer = { PropertyCommonizer(FunctionOrPropertyBaseCommonizer(TypeCommonizer(classifiers, settings))) },
|
||||
commonizerProducer = {
|
||||
PropertyCommonizer(FunctionOrPropertyBaseCommonizer(classifiers, settings, TypeCommonizer(classifiers, settings)))
|
||||
},
|
||||
nodeProducer = ::CirPropertyNode
|
||||
)
|
||||
|
||||
@@ -75,7 +77,7 @@ internal fun buildFunctionNode(
|
||||
nodeRelationship = nodeRelationship,
|
||||
commonizerProducer = {
|
||||
val typeCommonizer = TypeCommonizer(classifiers, settings)
|
||||
FunctionCommonizer(typeCommonizer, FunctionOrPropertyBaseCommonizer(typeCommonizer)).asCommonizer()
|
||||
FunctionCommonizer(typeCommonizer, FunctionOrPropertyBaseCommonizer(classifiers, settings, typeCommonizer)).asCommonizer()
|
||||
},
|
||||
nodeProducer = ::CirFunctionNode
|
||||
)
|
||||
|
||||
native/commonizer/testData/callableMemberCommonization/returnTypes/commonized/common/package_root.kt
Vendored
+4
@@ -28,9 +28,13 @@ expect fun function6(): Planet
|
||||
expect fun function7(): C
|
||||
|
||||
// Optimistic Number Commonization: KT-48455, KT-48568
|
||||
@kotlinx.cinterop.UnsafeNumber(["js: kotlin.Int", "jvm: kotlin.Long"])
|
||||
expect val propertyWithMismatchedType1: Int
|
||||
@kotlinx.cinterop.UnsafeNumber(["js: kotlin.Int", "jvm: kotlin.Short"])
|
||||
expect val propertyWithMismatchedType2: Short
|
||||
@kotlinx.cinterop.UnsafeNumber(["js: kotlin.Int", "jvm: kotlin.Long"])
|
||||
expect fun functionWithMismatchedType1(): Int
|
||||
@kotlinx.cinterop.UnsafeNumber(["js: kotlin.Int", "jvm: kotlin.Short"])
|
||||
expect fun functionWithMismatchedType2(): Short
|
||||
|
||||
expect class Box<T>(value: T) {
|
||||
|
||||
native/commonizer/testData/callableMemberCommonization/returnTypes/dependency/common/UnsafeNumber.kt
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
package kotlinx.cinterop
|
||||
@Target(AnnotationTarget.TYPEALIAS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class UnsafeNumber(val actualPlatformTypes: Array<String>)
|
||||
Vendored
+4
@@ -22,7 +22,11 @@ expect val property20: Char
|
||||
|
||||
// Optimistic Number Commonization: KT-48455, KT-48568
|
||||
// Mismatched const types should be commonized as expect val's
|
||||
@kotlinx.cinterop.UnsafeNumber(["js: kotlin.Short", "jvm: kotlin.Byte"])
|
||||
expect val property22: Byte
|
||||
@kotlinx.cinterop.UnsafeNumber(["js: kotlin.Int", "jvm: kotlin.Short"])
|
||||
expect val property23: Short
|
||||
@kotlinx.cinterop.UnsafeNumber(["js: kotlin.Long", "jvm: kotlin.Int"])
|
||||
expect val property24: Int
|
||||
@kotlinx.cinterop.UnsafeNumber(["js: kotlin.Float", "jvm: kotlin.Double"])
|
||||
expect val property26: Float
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
package kotlinx.cinterop
|
||||
@Target(AnnotationTarget.TYPEALIAS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class UnsafeNumber(val actualPlatformTypes: Array<String>)
|
||||
+78
-1
@@ -707,12 +707,18 @@ class HierarchicalOptimisticNumbersTypeCommonizerTest : AbstractInlineSourcesCom
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
setting(OptimisticNumberCommonizationEnabledKey, true)
|
||||
registerFakeStdlibIntegersDependency("(a, b)")
|
||||
registerDependency("a", "b", "(a, b)") { unsignedIntegers() }
|
||||
simpleSingleSourceTarget("a", "val x: UInt = null!!")
|
||||
simpleSingleSourceTarget("b", "val x: ULong = null!!")
|
||||
}
|
||||
|
||||
result.assertCommonized("(a, b)", "expect val x: kotlin.UInt")
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
@UnsafeNumber(["a: kotlin.UInt", "b: kotlin.ULong"])
|
||||
expect val x: kotlin.UInt
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
fun `test property with aliased number return type`() {
|
||||
@@ -790,4 +796,75 @@ class HierarchicalOptimisticNumbersTypeCommonizerTest : AbstractInlineSourcesCom
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
// return types, unlike parameter types, don't participate in function signature, and therefore can be commonized optimistically
|
||||
fun `test optimistic commonization in function return types`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
setting(OptimisticNumberCommonizationEnabledKey, true)
|
||||
registerFakeStdlibIntegersDependency(("(a, b)"))
|
||||
|
||||
"a" withSource """
|
||||
fun explicitReturnType(): Short {
|
||||
return 42
|
||||
}
|
||||
|
||||
fun implicitReturnType() = 42
|
||||
""".trimIndent()
|
||||
|
||||
"b" withSource """
|
||||
fun explicitReturnType(): Long {
|
||||
return 42L
|
||||
}
|
||||
|
||||
fun implicitReturnType() = 42L
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
import kotlinx.cinterop.*
|
||||
|
||||
@UnsafeNumber(["a: kotlin.Short", "b: kotlin.Long"])
|
||||
expect fun explicitReturnType(): Short
|
||||
|
||||
@UnsafeNumber(["a: kotlin.Int", "b: kotlin.Long"])
|
||||
expect fun implicitReturnType(): Int
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
fun `test optimistic commonization in property return types`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
setting(OptimisticNumberCommonizationEnabledKey, true)
|
||||
registerFakeStdlibIntegersDependency(("(a, b)"))
|
||||
|
||||
"a" withSource """
|
||||
val explicitReturnType: Short
|
||||
get() = 42
|
||||
|
||||
val implicitReturnType = 42
|
||||
""".trimIndent()
|
||||
|
||||
"b" withSource """
|
||||
val explicitReturnType: Long
|
||||
get() = 42L
|
||||
|
||||
val implicitReturnType = 42L
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
import kotlinx.cinterop.*
|
||||
|
||||
@UnsafeNumber(["a: kotlin.Short", "b: kotlin.Long"])
|
||||
expect val explicitReturnType: Short
|
||||
|
||||
@UnsafeNumber(["a: kotlin.Int", "b: kotlin.Long"])
|
||||
expect val implicitReturnType: Int
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -60,7 +60,7 @@ internal fun InlineSourceBuilder.ModuleBuilder.unsafeNumberAnnotationSource() {
|
||||
source(
|
||||
"""
|
||||
package kotlinx.cinterop
|
||||
@Target(AnnotationTarget.TYPEALIAS)
|
||||
@Target(AnnotationTarget.TYPEALIAS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class UnsafeNumber(val actualPlatformTypes: Array<String>)
|
||||
""".trimIndent(),
|
||||
|
||||
Reference in New Issue
Block a user