From 621f14c5bbe420c3b83db321387d05d701864aea Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Thu, 17 Mar 2022 16:40:27 +0300 Subject: [PATCH] [MPP] Improve creation of @UnsafeNumber w.r.t. non-aliased types and PIC ^KT-51643 --- .../kotlin/commonizer/cir/CirType.kt | 24 +- .../AbstractFunctionOrPropertyCommonizer.kt | 26 +-- ...irTypeRendererForUnsafeNumberAnnotation.kt | 46 ++++ .../kotlin/commonizer/core/CirTypeVisitor.kt | 68 ++++++ .../core/OptimisticNumbersTypeCommonizer.kt | 24 +- .../commonizer/core/TypeAliasCommonizer.kt | 16 +- .../core/UnsafeNumberAnnotationUtils.kt | 71 +++--- .../commonizer/mergedtree/nodeBuilders.kt | 2 +- ...hicalClassAndTypeAliasCommonizationTest.kt | 2 + ...icalOptimisticNumbersTypeCommonizerTest.kt | 77 ++++++- ...rchicalPlatformIntegerCommonizationTest.kt | 39 +--- ...UnsafeNumberAnnotationTypeRenderingTest.kt | 211 ++++++++++++++++++ 12 files changed, 494 insertions(+), 112 deletions(-) create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/core/CirTypeRendererForUnsafeNumberAnnotation.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/core/CirTypeVisitor.kt create mode 100644 native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/UnsafeNumberAnnotationTypeRenderingTest.kt diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirType.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirType.kt index 81b03059397..23ec68a6fbf 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirType.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirType.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.commonizer.utils.Interner import org.jetbrains.kotlin.commonizer.utils.appendHashCode import org.jetbrains.kotlin.commonizer.utils.hashCode import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull /** * The hierarchy of [CirType]: @@ -87,6 +88,9 @@ sealed class CirClassOrTypeAliasType : CirSimpleType(), AnyClassOrTypeAliasType abstract class CirClassType : CirClassOrTypeAliasType() { abstract val outerType: CirClassType? + abstract val attachments: List + + inline fun getAttachment(): T? = attachments.firstIsInstanceOrNull() override fun appendDescriptionTo(builder: StringBuilder, shortNameOnly: Boolean) { val outerType = outerType @@ -107,13 +111,15 @@ abstract class CirClassType : CirClassOrTypeAliasType() { classId: CirEntityId, outerType: CirClassType?, arguments: List, - isMarkedNullable: Boolean + isMarkedNullable: Boolean, + attachments: List = emptyList() ): CirClassType = interner.intern( CirClassTypeInternedImpl( classifierId = classId, outerType = outerType, arguments = arguments, - isMarkedNullable = isMarkedNullable + isMarkedNullable = isMarkedNullable, + attachments = attachments, ) ) @@ -121,13 +127,15 @@ abstract class CirClassType : CirClassOrTypeAliasType() { classifierId: CirEntityId = this.classifierId, outerType: CirClassType? = this.outerType, arguments: List = this.arguments, - isMarkedNullable: Boolean = this.isMarkedNullable + isMarkedNullable: Boolean = this.isMarkedNullable, + attachments: List = this.attachments, ): CirClassType { return createInterned( classId = classifierId, outerType = outerType, arguments = arguments, - isMarkedNullable = isMarkedNullable + isMarkedNullable = isMarkedNullable, + attachments = attachments, ) } @@ -217,12 +225,14 @@ private class CirClassTypeInternedImpl( override val outerType: CirClassType?, override val arguments: List, override val isMarkedNullable: Boolean, + override val attachments: List = emptyList(), ) : CirClassType() { private val hashCode = hashCode(classifierId) .appendHashCode(outerType) .appendHashCode(arguments) .appendHashCode(isMarkedNullable) + .appendHashCode(attachments.toTypedArray()) override fun hashCode(): Int = hashCode @@ -232,6 +242,7 @@ private class CirClassTypeInternedImpl( && isMarkedNullable == other.isMarkedNullable && arguments == other.arguments && outerType == other.outerType + && attachments == other.attachments else -> false } } @@ -262,3 +273,8 @@ private class CirTypeAliasTypeInternedImpl( else -> false } } + +/** + * Marker interface for arbitrary metadata that can be attached to a type during commonization + */ +interface CirTypeAttachment 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 fd20481f1f0..3ecda0e5fd5 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/AbstractFunctionOrPropertyCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/AbstractFunctionOrPropertyCommonizer.kt @@ -14,7 +14,6 @@ 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, @@ -49,11 +48,14 @@ class FunctionOrPropertyBaseCommonizer( return null } - val additionalUnsafeNumberAnnotation = - createUnsafeNumberAnnotationIfNecessary( - classifiers.classifierIndices.targets, settings, values, - getTypeIdFromDeclarationForCheck = ::getFunctionOrPropertyReturnTypeId, - ) + val returnType = returnTypeCommonizer(values) ?: return null + + val unsafeNumberAnnotation = createUnsafeNumberAnnotationIfNecessary( + classifiers.classifierIndices.targets, settings, + inputDeclarations = values, + inputTypes = values.map { it.returnType }, + commonizedType = returnType, + ) return FunctionOrProperty( name = values.first().name, @@ -63,17 +65,7 @@ class FunctionOrPropertyBaseCommonizer( extensionReceiver = (extensionReceiverCommonizer(values.map { it.extensionReceiver }) ?: return null).receiver, returnType = returnTypeCommonizer(values) ?: return null, typeParameters = TypeParameterListCommonizer(typeCommonizer).commonize(values.map { it.typeParameters }) ?: return null, - additionalAnnotations = listOfNotNull(additionalUnsafeNumberAnnotation) + additionalAnnotations = listOfNotNull(unsafeNumberAnnotation) ) } } - -private fun getFunctionOrPropertyReturnTypeId(functionOrProperty: CirFunctionOrProperty): CirEntityId? { - return functionOrProperty.returnType.let { returnType -> - when (returnType) { - is CirFlexibleType -> returnType.lowerBound.safeAs()?.classifierId - else -> returnType.safeAs()?.classifierId - } - } -} - diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/CirTypeRendererForUnsafeNumberAnnotation.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/CirTypeRendererForUnsafeNumberAnnotation.kt new file mode 100644 index 00000000000..ee1337f4d84 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/CirTypeRendererForUnsafeNumberAnnotation.kt @@ -0,0 +1,46 @@ +/* + * 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.cir.* +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty +import org.jetbrains.kotlin.utils.addToStdlib.ifTrue + +fun renderTypeForUnsafeNumberAnnotation(type: CirType): String = when (type) { + is CirSimpleType -> renderSimpleType(type) + is CirFlexibleType -> renderFlexibleType(type) +} + +private fun renderFlexibleType(type: CirFlexibleType): String = + "${renderTypeForUnsafeNumberAnnotation(type.lowerBound)}..${renderTypeForUnsafeNumberAnnotation(type.upperBound)}" + +private fun renderSimpleType(type: CirSimpleType): String { + return when (type) { + is CirTypeAliasType -> renderSimpleType(type.expandedType()) + is CirClassType -> "${type.classifierId.toQualifiedNameString()}${renderArguments(type.arguments)}${type.renderNullable()}" + is CirTypeParameterType -> "$TYPE_PARAMETER_TYPE_PREFIX${type.index}${type.renderNullable()}" + } +} + +private fun renderArguments(arguments: List): String { + return arguments.ifNotEmpty { joinToString(prefix = "<", postfix = ">") { renderTypeArgument(it) } }.orEmpty() +} + +private fun renderTypeArgument(typeArgument: CirTypeProjection): String { + return when (typeArgument) { + is CirRegularTypeProjection -> "${renderVariance(typeArgument.projectionKind)}${renderTypeForUnsafeNumberAnnotation(typeArgument.type)}" + CirStarTypeProjection -> STAR_PROJECTION + } +} + +private fun renderVariance(variance: Variance): String = "$variance ".takeIf { it.isNotBlank() }.orEmpty() + +private fun CirSimpleType.renderNullable(): String = isMarkedNullable.ifTrue { NULLABLE }.orEmpty() + +private const val NULLABLE: String = "?" +private const val STAR_PROJECTION: String = "*" +private const val TYPE_PARAMETER_TYPE_PREFIX = "#" diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/CirTypeVisitor.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/CirTypeVisitor.kt new file mode 100644 index 00000000000..d50ac4e2594 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/CirTypeVisitor.kt @@ -0,0 +1,68 @@ +/* + * 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.cir.* + +interface CirTypeVisitor { + fun visit(type: CirType) + fun visit(flexibleType: CirFlexibleType) + fun visit(simpleType: CirSimpleType) + fun visit(typeParameterType: CirTypeParameterType) + fun visit(classOrTypeAliasType: CirClassOrTypeAliasType) + fun visit(classType: CirClassType) + fun visit(typeAliasType: CirTypeAliasType) + fun visit(typeProjection: CirTypeProjection) +} + +open class BasicCirTypeVisitor : CirTypeVisitor { + override fun visit(type: CirType) { + return when (type) { + is CirFlexibleType -> visit(type) + is CirSimpleType -> visit(type) + } + } + + override fun visit(flexibleType: CirFlexibleType) { + visit(flexibleType.lowerBound) + visit(flexibleType.upperBound) + } + + override fun visit(simpleType: CirSimpleType) { + when (simpleType) { + is CirTypeParameterType -> visit(simpleType) + is CirClassOrTypeAliasType -> visit(simpleType) + } + } + + override fun visit(typeParameterType: CirTypeParameterType) { + } + + override fun visit(classOrTypeAliasType: CirClassOrTypeAliasType) { + when (classOrTypeAliasType) { + is CirClassType -> visit(classOrTypeAliasType) + is CirTypeAliasType -> visit(classOrTypeAliasType) + } + } + + override fun visit(classType: CirClassType) { + classType.outerType?.let { visit(it) } + classType.arguments.forEach { visit(it) } + } + + override fun visit(typeAliasType: CirTypeAliasType) { + visit(typeAliasType.underlyingType) + } + + override fun visit(typeProjection: CirTypeProjection) { + if (typeProjection is CirRegularTypeProjection) + visit(typeProjection.type) + } +} + +fun CirType.accept(visitor: CirTypeVisitor) { + visitor.visit(this) +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/OptimisticNumbersTypeCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/OptimisticNumbersTypeCommonizer.kt index b06f2599d06..6e7bec9361d 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/OptimisticNumbersTypeCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/OptimisticNumbersTypeCommonizer.kt @@ -5,8 +5,8 @@ package org.jetbrains.kotlin.commonizer.core -import org.jetbrains.kotlin.commonizer.cir.CirClassType -import org.jetbrains.kotlin.commonizer.cir.CirEntityId +import org.jetbrains.kotlin.commonizer.cir.* +import org.jetbrains.kotlin.commonizer.cir.CirClassType.Companion.copyInterned import org.jetbrains.kotlin.commonizer.utils.* private typealias BitWidth = Int @@ -72,25 +72,19 @@ private val floatingPointVars = SubstitutableNumbers( internal object OptimisticNumbersTypeCommonizer : AssociativeCommonizer { override fun commonize(first: CirClassType, second: CirClassType): CirClassType? { - return signedIntegers.choose(first, second) + val result = signedIntegers.choose(first, second) ?: unsignedIntegers.choose(first, second) ?: floatingPoints.choose(first, second) ?: signedVarIntegers.choose(first, second) ?: unsignedVarIntegers.choose(first, second) ?: floatingPointVars.choose(first, second) + + return result?.withMarker() } - fun isOptimisticallySubstitutable(classId: CirEntityId): Boolean { - val firstPackageSegment = classId.packageName.segments.firstOrNull() - if (firstPackageSegment != "kotlinx" && firstPackageSegment != "kotlin") { - return false - } + private fun CirClassType.withMarker(): CirClassType = this.copyInterned( + attachments = attachments + OptimisticCommonizationMarker + ) - return classId in signedIntegers - || classId in unsignedIntegers - || classId in floatingPoints - || classId in signedVarIntegers - || classId in unsignedVarIntegers - || classId in floatingPointVars - } + internal object OptimisticCommonizationMarker : CirTypeAttachment } \ No newline at end of file diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeAliasCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeAliasCommonizer.kt index 5e710f25636..5f125891b7f 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeAliasCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TypeAliasCommonizer.kt @@ -10,9 +10,9 @@ import org.jetbrains.kotlin.commonizer.cir.* import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers class TypeAliasCommonizer( - typeCommonizer: TypeCommonizer, private val classifiers: CirKnownClassifiers, private val settings: CommonizerSettings, + typeCommonizer: TypeCommonizer, ) : NullableSingleInvocationCommonizer { private val typeCommonizer = typeCommonizer.withContext { @@ -30,18 +30,20 @@ class TypeAliasCommonizer( val visibility = VisibilityCommonizer.lowering().commonize(values) ?: return null + val unsafeNumberAnnotation = createUnsafeNumberAnnotationIfNecessary( + classifiers.classifierIndices.targets, settings, + inputDeclarations = values, + inputTypes = values.map { it.underlyingType }, + commonizedType = underlyingType, + ) + return CirTypeAlias.create( name = name, typeParameters = typeParameters, visibility = visibility, underlyingType = underlyingType, expandedType = underlyingType.expandedType(), - annotations = listOfNotNull( - createUnsafeNumberAnnotationIfNecessary( - classifiers.classifierIndices.targets, settings, values, - getTypeIdFromDeclarationForCheck = { typeAlias -> typeAlias.expandedType.classifierId } - ) - ) + annotations = listOfNotNull(unsafeNumberAnnotation), ) } } diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/UnsafeNumberAnnotationUtils.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/UnsafeNumberAnnotationUtils.kt index e52a5adf4a5..9346b1e2654 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/UnsafeNumberAnnotationUtils.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/UnsafeNumberAnnotationUtils.kt @@ -12,44 +12,28 @@ import org.jetbrains.kotlin.commonizer.allLeaves import org.jetbrains.kotlin.commonizer.cir.* import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull -internal fun createUnsafeNumberAnnotationIfNecessary( +fun createUnsafeNumberAnnotationIfNecessary( targets: List, settings: CommonizerSettings, - values: List, - getTypeIdFromDeclarationForCheck: (declaration: T) -> CirEntityId?, + inputDeclarations: List, + inputTypes: List, + commonizedType: CirType, ): CirAnnotation? { - val isOptimisticCommonizationEnabled = settings.getSetting(OptimisticNumberCommonizationEnabledKey) - - if (!isOptimisticCommonizationEnabled) + if (!shouldCreateAnnotation(settings, commonizedType, inputDeclarations)) return null - val typeIds = values.map { annotated -> getTypeIdFromDeclarationForCheck(annotated) } + val actualPlatformTypes = mutableMapOf() - // 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 + inputTypes.zip(targets).forEach { (type, target) -> + target.allLeaves().forEach { leafCommonizerTarget -> + actualPlatformTypes[leafCommonizerTarget.name] = renderTypeForUnsafeNumberAnnotation(type) + } } - return createUnsafeNumberAnnotation(targets, values, getTypeIdFromDeclarationForCheck) -} - -private fun createUnsafeNumberAnnotation( - targets: List, - values: List, - getTypeIdFromDeclaration: (declaration: T) -> CirEntityId?, -): CirAnnotation? { - val actualPlatformTypes = mutableMapOf() - - values.forEachIndexed forEach@{ index, annotated -> + inputDeclarations.forEach { annotated -> val existingAnnotation = annotated.annotations.firstIsInstanceOrNull() 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") } } @@ -60,14 +44,43 @@ private fun createUnsafeNumberAnnotation( return null } -private class UnsafeNumberAnnotation(val actualPlatformTypes: Map) : CirAnnotation { +private fun shouldCreateAnnotation( + settings: CommonizerSettings, + commonizedType: CirType, + inputDeclarations: List, +): Boolean { + if (!settings.getSetting(OptimisticNumberCommonizationEnabledKey)) + return false + + val annotatedInputDeclarationPresent = inputDeclarations.any { declaration -> + declaration.annotations.any { annotation -> annotation is UnsafeNumberAnnotation } + } + + if (annotatedInputDeclarationPresent) + return true + + var isMarkedTypeFound = false + + commonizedType.accept(object : BasicCirTypeVisitor() { + override fun visit(classType: CirClassType) { + classType.getAttachment()?.let { isMarkedTypeFound = true } + ?: super.visit(classType) + } + }) + + return isMarkedTypeFound +} + +private typealias RenderedType = String + +private class UnsafeNumberAnnotation(val actualPlatformTypes: Map) : CirAnnotation { override val type: CirClassType = UnsafeNumberAnnotation.type override val annotationValueArguments: Map = emptyMap() override val constantValueArguments: Map = mapOf( CirName.create("actualPlatformTypes") to CirConstantValue.ArrayValue( actualPlatformTypes.toSortedMap().map { (platform, type) -> - CirConstantValue.StringValue("$platform: ${type.toQualifiedNameString()}") + CirConstantValue.StringValue("$platform: $type") } ) ) diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/nodeBuilders.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/nodeBuilders.kt index a33528fa8e5..892250d8049 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/nodeBuilders.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/nodeBuilders.kt @@ -130,7 +130,7 @@ internal fun buildTypeAliasNode( storageManager = storageManager, size = size, nodeRelationship = null, - commonizerProducer = { TypeAliasCommonizer(TypeCommonizer(classifiers, settings), classifiers, settings = settings).asCommonizer() }, + commonizerProducer = { TypeAliasCommonizer(classifiers, settings, TypeCommonizer(classifiers, settings)).asCommonizer() }, recursionMarker = CirTypeAliasRecursionMarker, nodeProducer = { targetDeclarations, commonDeclaration -> CirTypeAliasNode(typeAliasId, targetDeclarations, commonDeclaration).also { diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalClassAndTypeAliasCommonizationTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalClassAndTypeAliasCommonizationTest.kt index 058167ef7b7..a59dff7ba9f 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalClassAndTypeAliasCommonizationTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalClassAndTypeAliasCommonizationTest.kt @@ -575,6 +575,7 @@ class HierarchicalClassAndTypeAliasCommonizationTest : AbstractInlineSourcesComm typealias Proxy = Short @UnsafeNumber(["c: kotlin.Int", "d: kotlin.Short"]) typealias X = Proxy + @UnsafeNumber(["c: kotlin.Int", "d: kotlin.Short"]) expect val x: X """.trimIndent() ) @@ -585,6 +586,7 @@ class HierarchicalClassAndTypeAliasCommonizationTest : AbstractInlineSourcesComm typealias Proxy = Short @UnsafeNumber(["a: kotlin.Long", "b: kotlin.Long", "c: kotlin.Int", "d: kotlin.Short"]) typealias X = Proxy + @UnsafeNumber(["a: kotlin.Long", "b: kotlin.Long", "c: kotlin.Int", "d: kotlin.Short"]) expect val x: X """.trimIndent() ) diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalOptimisticNumbersTypeCommonizerTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalOptimisticNumbersTypeCommonizerTest.kt index 2319a0eb653..6a757908b05 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalOptimisticNumbersTypeCommonizerTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalOptimisticNumbersTypeCommonizerTest.kt @@ -595,7 +595,7 @@ class HierarchicalOptimisticNumbersTypeCommonizerTest : AbstractInlineSourcesCom result.assertCommonized( "(a, b)", """ - @UnsafeNumber(["a: kotlinx.cinterop.UIntVarOf", "b: kotlinx.cinterop.ULongVarOf"]) + @UnsafeNumber(["a: kotlinx.cinterop.UIntVarOf", "b: kotlinx.cinterop.ULongVarOf"]) typealias X = kotlinx.cinterop.UIntVarOf """.trimIndent() ) @@ -612,7 +612,7 @@ class HierarchicalOptimisticNumbersTypeCommonizerTest : AbstractInlineSourcesCom result.assertCommonized( "(a, b)", """ - @UnsafeNumber(["a: kotlinx.cinterop.IntVarOf", "b: kotlinx.cinterop.LongVarOf"]) + @UnsafeNumber(["a: kotlinx.cinterop.IntVarOf", "b: kotlinx.cinterop.LongVarOf"]) typealias X = kotlinx.cinterop.IntVarOf """.trimIndent() ) @@ -744,6 +744,7 @@ class HierarchicalOptimisticNumbersTypeCommonizerTest : AbstractInlineSourcesCom "(a, b)", """ @UnsafeNumber(["a: kotlin.UShort", "b: kotlin.ULong"]) typealias X = UShort + @UnsafeNumber(["a: kotlin.UShort", "b: kotlin.ULong"]) expect val x: X """.trimIndent() ) @@ -867,4 +868,76 @@ class HierarchicalOptimisticNumbersTypeCommonizerTest : AbstractInlineSourcesCom """.trimIndent() ) } + + fun `test optimistic commonization inside parameterized types`() { + val result = commonize { + outputTarget("(a, b)", "(c, d)", "(a, b, c, d)") + setting(OptimisticNumberCommonizationEnabledKey, true) + registerFakeStdlibIntegersDependency("(a, b)", "(c, d)", "(a, b, c, d)") + + "a" withSource """ + typealias TA = Int + class Box + + fun x(): Box = Box() + fun y(a: TA) {} + """.trimIndent() + + "b" withSource """ + typealias TA = Long + class Box + + fun x(): Box = Box() + fun y(a: TA) {} + """.trimIndent() + + "c" withSource """ + class Box + fun x(): Box = Box() + fun y(a: Int) {} + """.trimIndent() + + "d" withSource """ + class Box + fun x(): Box = Box() + fun y(a: Long) {} + """.trimIndent() + } + + result.assertCommonized( + "(a, b)", """ + import kotlinx.cinterop.* + + expect class Box() + @UnsafeNumber(["a: kotlin.Int", "b: kotlin.Long"]) + typealias TA = Int + + @UnsafeNumber(["a: Box", "b: Box"]) + expect fun x(): Box + expect fun y(a: TA) + """.trimIndent() + ) + + result.assertCommonized( + "(c, d)", """ + import kotlinx.cinterop.* + + expect class Box() + + @UnsafeNumber(["c: Box", "d: Box"]) + expect fun x(): Box + """.trimIndent() + ) + + result.assertCommonized( + "(a, b, c, d)", """ + import kotlinx.cinterop.* + + expect class Box() + + @UnsafeNumber(["a: Box", "b: Box", "c: Box", "d: Box"]) + expect fun x(): Box + """.trimIndent() + ) + } } \ No newline at end of file diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalPlatformIntegerCommonizationTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalPlatformIntegerCommonizationTest.kt index 42264a67e5e..944ffe068c6 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalPlatformIntegerCommonizationTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalPlatformIntegerCommonizationTest.kt @@ -60,7 +60,6 @@ class HierarchicalPlatformIntegerCommonizationTest : AbstractInlineSourcesCommon "(${LINUX_ARM64.name}, ${LINUX_ARM32_HFP.name})", """ @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlin.Long", "${LINUX_ARM64.name}: kotlin.Int"]) typealias X = Int - @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlin.Int", "${LINUX_ARM64.name}: kotlin.Long"]) typealias Y = PlatformInt """.trimIndent() ) @@ -87,7 +86,6 @@ class HierarchicalPlatformIntegerCommonizationTest : AbstractInlineSourcesCommon "(${LINUX_ARM64.name}, ${LINUX_ARM32_HFP.name})", """ @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlin.ULong", "${LINUX_ARM64.name}: kotlin.UInt"]) typealias X = UInt - @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlin.UInt", "${LINUX_ARM64.name}: kotlin.ULong"]) typealias Y = PlatformUInt """.trimIndent() ) @@ -124,11 +122,9 @@ class HierarchicalPlatformIntegerCommonizationTest : AbstractInlineSourcesCommon @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlin.Long", "${LINUX_ARM64.name}: kotlin.Int"]) typealias AX = Int - @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlin.Int", "${LINUX_ARM64.name}: kotlin.Long"]) typealias AY = PlatformInt - @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlinx.cinterop.LongVarOf", "${LINUX_ARM64.name}: kotlinx.cinterop.IntVarOf"]) + @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlinx.cinterop.LongVarOf", "${LINUX_ARM64.name}: kotlinx.cinterop.IntVarOf"]) typealias X = IntVarOf - @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlinx.cinterop.IntVarOf", "${LINUX_ARM64.name}: kotlinx.cinterop.LongVarOf"]) typealias Y = PlatformIntVarOf """.trimIndent() ) @@ -165,11 +161,9 @@ class HierarchicalPlatformIntegerCommonizationTest : AbstractInlineSourcesCommon @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlin.ULong", "${LINUX_ARM64.name}: kotlin.UInt"]) typealias AX = UInt - @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlin.UInt", "${LINUX_ARM64.name}: kotlin.ULong"]) typealias AY = PlatformUInt - @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlinx.cinterop.ULongVarOf", "${LINUX_ARM64.name}: kotlinx.cinterop.UIntVarOf"]) + @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlinx.cinterop.ULongVarOf", "${LINUX_ARM64.name}: kotlinx.cinterop.UIntVarOf"]) typealias X = UIntVarOf - @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlinx.cinterop.UIntVarOf", "${LINUX_ARM64.name}: kotlinx.cinterop.ULongVarOf"]) typealias Y = PlatformUIntVarOf """.trimIndent() ) @@ -386,9 +380,7 @@ class HierarchicalPlatformIntegerCommonizationTest : AbstractInlineSourcesCommon import kotlinx.cinterop.* expect class C() { - @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlin.Int", "${LINUX_ARM64.name}: kotlin.Long"]) val i: PlatformInt - @UnsafeNumber(["${LINUX_ARM32_HFP.name}: kotlinx.cinterop.IntVarOf", "${LINUX_ARM64.name}: kotlinx.cinterop.LongVarOf"]) fun v(): PlatformIntVarOf fun r(): PlatformIntRange } @@ -549,9 +541,7 @@ class HierarchicalPlatformIntegerCommonizationTest : AbstractInlineSourcesCommon "($intTarget3, $longTarget3)", """ import kotlinx.cinterop.* - @UnsafeNumber(["${LINUX_MIPSEL32.name}: kotlin.Int", "${MACOS_X64.name}: kotlin.Long"]) typealias X = PlatformInt - @UnsafeNumber(["${LINUX_MIPSEL32.name}: kotlinx.cinterop.IntVarOf", "${MACOS_X64.name}: kotlinx.cinterop.LongVarOf"]) typealias XV = PlatformIntVarOf """.trimIndent() ) @@ -560,15 +550,7 @@ class HierarchicalPlatformIntegerCommonizationTest : AbstractInlineSourcesCommon "($intTarget1, $intTarget2, $longTarget1, $longTarget2)", """ import kotlinx.cinterop.* - @UnsafeNumber([ - "${LINUX_ARM32_HFP.name}: kotlin.Int", "${LINUX_ARM64.name}: kotlin.Long", - "${LINUX_MIPS32.name}: kotlin.Int", "${LINUX_X64.name}: kotlin.Long" - ]) typealias X = PlatformInt - @UnsafeNumber([ - "${LINUX_ARM32_HFP.name}: kotlinx.cinterop.IntVarOf", "${LINUX_ARM64.name}: kotlinx.cinterop.LongVarOf", - "${LINUX_MIPS32.name}: kotlinx.cinterop.IntVarOf", "${LINUX_X64.name}: kotlinx.cinterop.LongVarOf" - ]) typealias XV = PlatformIntVarOf """.trimIndent() ) @@ -577,15 +559,7 @@ class HierarchicalPlatformIntegerCommonizationTest : AbstractInlineSourcesCommon "($intTarget1, $intTarget2, $intTarget3, $longTarget1)", """ import kotlinx.cinterop.* - @UnsafeNumber([ - "${LINUX_ARM32_HFP.name}: kotlin.Int", "${LINUX_MIPS32.name}: kotlin.Int", - "${LINUX_MIPSEL32.name}: kotlin.Int", "${LINUX_X64.name}: kotlin.Long" - ]) typealias X = PlatformInt - @UnsafeNumber([ - "${LINUX_ARM32_HFP.name}: kotlinx.cinterop.IntVarOf", "${LINUX_MIPS32.name}: kotlinx.cinterop.IntVarOf", - "${LINUX_MIPSEL32.name}: kotlinx.cinterop.IntVarOf", "${LINUX_X64.name}: kotlinx.cinterop.LongVarOf" - ]) typealias XV = PlatformIntVarOf """.trimIndent() ) @@ -594,15 +568,7 @@ class HierarchicalPlatformIntegerCommonizationTest : AbstractInlineSourcesCommon "($longTarget1, $longTarget2, $longTarget3, $intTarget1)", """ import kotlinx.cinterop.* - @UnsafeNumber([ - "${LINUX_ARM32_HFP.name}: kotlin.Int", "${LINUX_ARM64.name}: kotlin.Long", - "${LINUX_X64.name}: kotlin.Long", "${MACOS_X64.name}: kotlin.Long" - ]) typealias X = PlatformInt - @UnsafeNumber([ - "${LINUX_ARM32_HFP.name}: kotlinx.cinterop.IntVarOf", "${LINUX_ARM64.name}: kotlinx.cinterop.LongVarOf", - "${LINUX_X64.name}: kotlinx.cinterop.LongVarOf", "${MACOS_X64.name}: kotlinx.cinterop.LongVarOf" - ]) typealias XV = PlatformIntVarOf """.trimIndent() ) @@ -706,7 +672,6 @@ class HierarchicalPlatformIntegerCommonizationTest : AbstractInlineSourcesCommon result.assertCommonized( "(${IOS_ARM32.name}, ${IOS_ARM64.name})", """ - @UnsafeNumber(["${IOS_ARM32.name}: kotlin.UInt", "${IOS_ARM64.name}: kotlin.ULong"]) typealias OtherAlias = PlatformUInt expect class Box() diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/UnsafeNumberAnnotationTypeRenderingTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/UnsafeNumberAnnotationTypeRenderingTest.kt new file mode 100644 index 00000000000..153ce1b6481 --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/UnsafeNumberAnnotationTypeRenderingTest.kt @@ -0,0 +1,211 @@ +/* + * 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.hierarchical + +import org.jetbrains.kotlin.commonizer.cir.* +import org.jetbrains.kotlin.commonizer.core.renderTypeForUnsafeNumberAnnotation +import org.jetbrains.kotlin.types.Variance +import org.junit.Test +import kotlin.test.assertEquals + +class UnsafeNumberAnnotationTypeRenderingTest { + @Test + fun `test simple class type with no arguments`() { + val type = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/String"), outerType = null, arguments = emptyList(), isMarkedNullable = false, + ) + assertEquals("kotlin.String", renderTypeForUnsafeNumberAnnotation(type)) + } + + @Test + fun `test simple nullable class type`() { + val type = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/String"), outerType = null, arguments = emptyList(), isMarkedNullable = true, + ) + assertEquals("kotlin.String?", renderTypeForUnsafeNumberAnnotation(type)) + } + + @Test + fun `test expanded type alias type with no arguments`() { + val type = CirTypeAliasType.createInterned( + typeAliasId = CirEntityId.create("T"), + underlyingType = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/String"), outerType = null, arguments = emptyList(), isMarkedNullable = false, + ), + arguments = emptyList(), + isMarkedNullable = false, + ) + assertEquals("kotlin.String", renderTypeForUnsafeNumberAnnotation(type)) + } + + @Test + fun `test class type with arguments`() { + val type = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/Triple"), outerType = null, isMarkedNullable = false, + arguments = listOf( + CirRegularTypeProjection( + projectionKind = Variance.INVARIANT, type = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/String"), outerType = null, arguments = emptyList(), isMarkedNullable = false, + ) + ), + CirRegularTypeProjection( + projectionKind = Variance.OUT_VARIANCE, type = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/Number"), outerType = null, arguments = emptyList(), isMarkedNullable = false, + ) + ), + CirStarTypeProjection, + ), + ) + assertEquals("kotlin.Triple", renderTypeForUnsafeNumberAnnotation(type)) + } + + @Test + fun `test type alias type with an argument`() { + val type = CirTypeAliasType.createInterned( + typeAliasId = CirEntityId.create("T"), isMarkedNullable = false, arguments = emptyList(), + underlyingType = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/Triple"), outerType = null, isMarkedNullable = false, + arguments = listOf( + CirRegularTypeProjection( + projectionKind = Variance.INVARIANT, type = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/String"), + outerType = null, + arguments = emptyList(), + isMarkedNullable = false, + ) + ), + CirRegularTypeProjection( + projectionKind = Variance.OUT_VARIANCE, type = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/Number"), + outerType = null, + arguments = emptyList(), + isMarkedNullable = false, + ) + ), + CirStarTypeProjection, + ), + ), + ) + assertEquals("kotlin.Triple", renderTypeForUnsafeNumberAnnotation(type)) + } + + @Test + fun `test flexible type`() { + val type = CirFlexibleType( + lowerBound = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/String"), outerType = null, arguments = emptyList(), isMarkedNullable = false, + ), + upperBound = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/String"), outerType = null, arguments = emptyList(), isMarkedNullable = true, + ) + ) + assertEquals("kotlin.String..kotlin.String?", renderTypeForUnsafeNumberAnnotation(type)) + } + + @Test + fun `test flexible type argument`() { + val type = CirClassType.createInterned( + classId = CirEntityId.create("kotlin.Array"), outerType = null, isMarkedNullable = false, arguments = listOf( + CirRegularTypeProjection( + projectionKind = Variance.OUT_VARIANCE, + type = CirFlexibleType( + lowerBound = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/String"), + outerType = null, + arguments = emptyList(), + isMarkedNullable = false, + ), + upperBound = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/String"), + outerType = null, + arguments = emptyList(), + isMarkedNullable = true, + ) + ) + ) + ) + ) + assertEquals("kotlin.Array", renderTypeForUnsafeNumberAnnotation(type)) + } + + @Test + fun `test nested type argument`() { + val type = CirClassType.createInterned( + classId = CirEntityId.create("kotlin.Array"), outerType = null, isMarkedNullable = false, arguments = listOf( + CirRegularTypeProjection( + projectionKind = Variance.OUT_VARIANCE, + type = CirClassType.createInterned( + classId = CirEntityId.create("kotlin.Array"), outerType = null, isMarkedNullable = false, arguments = listOf( + CirRegularTypeProjection( + projectionKind = Variance.IN_VARIANCE, + type = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/String"), + outerType = null, + arguments = emptyList(), + isMarkedNullable = false, + ) + ) + ) + ) + ) + ) + ) + + assertEquals("kotlin.Array>", renderTypeForUnsafeNumberAnnotation(type)) + } + + @Test + fun `test type parameter type in type argument`() { + val type = CirClassType.createInterned( + classId = CirEntityId.create("kotlin.Array"), outerType = null, isMarkedNullable = false, arguments = listOf( + CirRegularTypeProjection( + projectionKind = Variance.INVARIANT, + type = CirTypeParameterType.createInterned(index = 0, isMarkedNullable = true) + ) + ) + ) + + assertEquals("kotlin.Array<#0?>", renderTypeForUnsafeNumberAnnotation(type)) + } + + @Test + fun `test combined type`() { + val type = CirClassType.createInterned( + classId = CirEntityId.create("kotlin.Array"), outerType = null, isMarkedNullable = false, arguments = listOf( + CirRegularTypeProjection( + projectionKind = Variance.OUT_VARIANCE, + type = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/Pair"), outerType = null, isMarkedNullable = true, + arguments = listOf( + CirStarTypeProjection, + CirRegularTypeProjection( + projectionKind = Variance.IN_VARIANCE, type = CirFlexibleType( + lowerBound = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/String"), + outerType = null, + arguments = emptyList(), + isMarkedNullable = false, + ), + upperBound = CirClassType.createInterned( + classId = CirEntityId.create("kotlin/String"), + outerType = null, + arguments = emptyList(), + isMarkedNullable = true, + ) + ) + ), + ), + ) + ) + ) + ) + + assertEquals( + "kotlin.Array?>", + renderTypeForUnsafeNumberAnnotation(type) + ) + } +}