[Commonizer] Emit 'UnsafeNumber' for optimistically commonized typealias
^KT-48459 Verification Pending
This commit is contained in:
committed by
Space
parent
bff62aba5f
commit
f7839e847a
@@ -5,7 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.core
|
||||
|
||||
interface Commonizer<T, R> {
|
||||
interface Commonizer<T, out R> {
|
||||
val result: R
|
||||
fun commonizeWith(next: T): Boolean
|
||||
}
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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 NullableSingleInvocationCommonizer<T : Any> {
|
||||
operator fun invoke(values: List<T>): T?
|
||||
}
|
||||
|
||||
fun <T : Any> NullableSingleInvocationCommonizer<T>.asCommonizer(): Commonizer<T, T?> = object : Commonizer<T, T?> {
|
||||
private val collectedValues = mutableListOf<T>()
|
||||
|
||||
override val result: T?
|
||||
get() = this@asCommonizer.invoke(collectedValues)
|
||||
|
||||
override fun commonizeWith(next: T): Boolean {
|
||||
collectedValues.add(next)
|
||||
return true
|
||||
}
|
||||
}
|
||||
+16
-1
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.commonizer.cir.CirEntityId
|
||||
private typealias BitWidth = Int
|
||||
|
||||
private class SubstitutableNumbers(private val numbers: Map<CirEntityId, BitWidth>) {
|
||||
operator fun contains(id: CirEntityId) = id in numbers
|
||||
fun choose(first: CirClassType, second: CirClassType): CirClassType? {
|
||||
val firstBitWidth = numbers[first.classifierId] ?: return null
|
||||
val secondBitWidth = numbers[second.classifierId] ?: return null
|
||||
@@ -68,7 +69,7 @@ private val floatingPointVars = SubstitutableNumbers(
|
||||
)
|
||||
)
|
||||
|
||||
object OptimisticNumbersTypeCommonizer : AssociativeCommonizer<CirClassType> {
|
||||
internal object OptimisticNumbersTypeCommonizer : AssociativeCommonizer<CirClassType> {
|
||||
override fun commonize(first: CirClassType, second: CirClassType): CirClassType? {
|
||||
return signedIntegers.choose(first, second)
|
||||
?: unsignedIntegers.choose(first, second)
|
||||
@@ -77,4 +78,18 @@ object OptimisticNumbersTypeCommonizer : AssociativeCommonizer<CirClassType> {
|
||||
?: unsignedVarIntegers.choose(first, second)
|
||||
?: floatingPointVars.choose(first, second)
|
||||
}
|
||||
|
||||
fun isOptimisticallySubstitutable(classId: CirEntityId): Boolean {
|
||||
val firstPackageSegment = classId.packageName.segments.firstOrNull()
|
||||
if (firstPackageSegment != "kotlinx" && firstPackageSegment != "kotlin") {
|
||||
return false
|
||||
}
|
||||
|
||||
return classId in signedIntegers
|
||||
|| classId in unsignedIntegers
|
||||
|| classId in floatingPoints
|
||||
|| classId in signedVarIntegers
|
||||
|| classId in unsignedVarIntegers
|
||||
|| classId in floatingPointVars
|
||||
}
|
||||
}
|
||||
@@ -5,32 +5,91 @@
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.core
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirClassOrTypeAliasType
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirTypeAlias
|
||||
import org.jetbrains.kotlin.commonizer.CommonizerTarget
|
||||
import org.jetbrains.kotlin.commonizer.allLeaves
|
||||
import org.jetbrains.kotlin.commonizer.cir.*
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
|
||||
class TypeAliasCommonizer(
|
||||
private val classifiers: CirKnownClassifiers
|
||||
) : AssociativeCommonizer<CirTypeAlias> {
|
||||
) : NullableSingleInvocationCommonizer<CirTypeAlias> {
|
||||
override fun invoke(values: List<CirTypeAlias>): CirTypeAlias? {
|
||||
if (values.isEmpty()) return null
|
||||
|
||||
override fun commonize(first: CirTypeAlias, second: CirTypeAlias): CirTypeAlias? {
|
||||
val name = if (first.name == second.name) first.name else return null
|
||||
val name = values.map { it.name }.distinct().singleOrNull() ?: return null
|
||||
|
||||
val typeParameters = TypeParameterListCommonizer(classifiers)
|
||||
.commonize(listOf(first.typeParameters, second.typeParameters)) ?: return null
|
||||
val typeParameters = TypeParameterListCommonizer(classifiers).commonize(values.map { it.typeParameters }) ?: return null
|
||||
|
||||
val underlyingType = TypeCommonizer(classifiers, TypeCommonizer.Options.default.withAllowOptimisticNumberTypeCommonization())
|
||||
.commonize(first.underlyingType, second.underlyingType) as? CirClassOrTypeAliasType ?: return null
|
||||
.asCommonizer().commonize(values.map { it.underlyingType }) as? CirClassOrTypeAliasType ?: return null
|
||||
|
||||
val visibility = VisibilityCommonizer.lowering().commonize(listOf(first, second)) ?: return null
|
||||
val visibility = VisibilityCommonizer.lowering().commonize(values) ?: return null
|
||||
|
||||
return CirTypeAlias.create(
|
||||
annotations = emptyList(),
|
||||
name = name,
|
||||
typeParameters = typeParameters,
|
||||
visibility = visibility,
|
||||
underlyingType = underlyingType,
|
||||
expandedType = underlyingType.expandedType()
|
||||
expandedType = underlyingType.expandedType(),
|
||||
annotations = listOfNotNull(
|
||||
createUnsafeNumberAnnotationIfNecessary(classifiers.classifierIndices.targets, values)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createUnsafeNumberAnnotationIfNecessary(
|
||||
targets: List<CommonizerTarget>,
|
||||
values: List<CirTypeAlias>,
|
||||
): CirAnnotation? {
|
||||
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") }
|
||||
)
|
||||
)
|
||||
|
||||
companion object {
|
||||
private val type = CirClassType.createInterned(
|
||||
classId = CirEntityId.create("kotlinx/cinterop/UnsafeNumber"),
|
||||
outerType = null,
|
||||
visibility = Visibilities.Public,
|
||||
arguments = emptyList(),
|
||||
isMarkedNullable = false
|
||||
)
|
||||
|
||||
val empty = UnsafeNumberAnnotation(emptyMap())
|
||||
}
|
||||
}
|
||||
@@ -129,7 +129,7 @@ private fun <T : CirDeclaration, R : CirDeclaration, N : CirNode<T, R>> buildNod
|
||||
storageManager: StorageManager,
|
||||
size: Int,
|
||||
nodeRelationship: CirNodeRelationship?,
|
||||
commonizerProducer: () -> Commonizer<T, R>,
|
||||
commonizerProducer: () -> Commonizer<T, R?>,
|
||||
recursionMarker: R? = null,
|
||||
nodeProducer: (CommonizedGroup<T>, NullableLazyValue<R>) -> N
|
||||
): N {
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.hierarchical
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.AbstractInlineSourcesCommonizationTest
|
||||
import org.jetbrains.kotlin.commonizer.assertCommonized
|
||||
import org.jetbrains.kotlin.commonizer.utils.InlineSourceBuilder
|
||||
|
||||
class HierarchicalUnsafeNumberAnnotationCommonizationTest : AbstractInlineSourcesCommonizationTest() {
|
||||
|
||||
fun `test multiple targets`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)", "(c, d)", "(a, b, c, d)")
|
||||
registerDependency("a", "b", "c", "d", "(a, b)", "(c, d)", "(a, b, c, d)") { unsafeNumberAnnotationSource() }
|
||||
simpleSingleSourceTarget("a", "typealias X = Short")
|
||||
simpleSingleSourceTarget("b", "typealias X = Int")
|
||||
simpleSingleSourceTarget("c", "typealias X = Int")
|
||||
simpleSingleSourceTarget("d", "typealias X = Long")
|
||||
}
|
||||
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
@kotlinx.cinterop.UnsafeNumber(["a: kotlin/Short", "b: kotlin/Int"])
|
||||
typealias X = Int
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
result.assertCommonized(
|
||||
"(c, d)", """
|
||||
import kotlinx.cinterop.*
|
||||
@UnsafeNumber(["c: kotlin/Int", "d: kotlin/Long"])
|
||||
typealias X = Long
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
result.assertCommonized(
|
||||
"(a, b, c, d)", """
|
||||
import kotlinx.cinterop.*
|
||||
@UnsafeNumber(["a: kotlin/Short", "b: kotlin/Int", "c: kotlin/Int", "d: kotlin/Long"])
|
||||
typealias X = Long
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun InlineSourceBuilder.ModuleBuilder.unsafeNumberAnnotationSource() {
|
||||
source(
|
||||
"""
|
||||
package kotlinx.cinterop
|
||||
@Target(AnnotationTarget.TYPEALIAS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class UnsafeNumber(val actualPlatformTypes: Array<String>)
|
||||
""".trimIndent(),
|
||||
"UnsafeNumberAnnotation.kt"
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user