Fix .equals for instantiated annotations with unsigned array types.

#KT-53876 Fixed
This commit is contained in:
Leonid Startsev
2022-09-28 17:19:34 +02:00
committed by Space Team
parent 0fea8073ef
commit ccdb6fc599
12 changed files with 92 additions and 13 deletions
@@ -492,6 +492,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/annotations/instances/annotationType.kt"); runTest("compiler/testData/codegen/box/annotations/instances/annotationType.kt");
} }
@Test
@TestMetadata("annotationsUnsignedTypes.kt")
public void testAnnotationsUnsignedTypes() throws Exception {
runTest("compiler/testData/codegen/box/annotations/instances/annotationsUnsignedTypes.kt");
}
@Test @Test
@TestMetadata("inInlineFunction.kt") @TestMetadata("inInlineFunction.kt")
public void testInInlineFunction() throws Exception { public void testInInlineFunction() throws Exception {
@@ -42,7 +42,8 @@ class AnnotationImplementationLowering(
} }
} }
abstract class AnnotationImplementationTransformer(val context: BackendContext, val irFile: IrFile?) : IrElementTransformerVoidWithContext() { abstract class AnnotationImplementationTransformer(val context: BackendContext, val irFile: IrFile?) :
IrElementTransformerVoidWithContext() {
internal val implementations: MutableMap<IrClass, IrClass> = mutableMapOf() internal val implementations: MutableMap<IrClass, IrClass> = mutableMapOf()
@@ -72,7 +73,7 @@ abstract class AnnotationImplementationTransformer(val context: BackendContext,
} }
} }
abstract fun chooseConstructor(implClass: IrClass, expression: IrConstructorCall) : IrConstructor abstract fun chooseConstructor(implClass: IrClass, expression: IrConstructorCall): IrConstructor
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
val constructedClass = expression.type.classOrNull?.owner ?: return super.visitConstructorCall(expression) val constructedClass = expression.type.classOrNull?.owner ?: return super.visitConstructorCall(expression)
@@ -104,8 +105,10 @@ abstract class AnnotationImplementationTransformer(val context: BackendContext,
destination.symbol.owner.valueParameters.forEachIndexed { index, parameter -> destination.symbol.owner.valueParameters.forEachIndexed { index, parameter ->
val valueArg = argumentsByName[parameter.name] val valueArg = argumentsByName[parameter.name]
if (parameter.defaultValue == null && valueArg == null) if (parameter.defaultValue == null && valueArg == null)
error("Usage of default value argument for this annotation is not yet possible.\n" + error(
"Please specify value for '${source.type.classOrNull?.owner?.name}.${parameter.name}' explicitly") "Usage of default value argument for this annotation is not yet possible.\n" +
"Please specify value for '${source.type.classOrNull?.owner?.name}.${parameter.name}' explicitly"
)
destination.putValueArgument(index, valueArg) destination.putValueArgument(index, valueArg)
} }
} }
@@ -159,18 +162,22 @@ abstract class AnnotationImplementationTransformer(val context: BackendContext,
abstract fun getArrayContentEqualsSymbol(type: IrType): IrFunctionSymbol abstract fun getArrayContentEqualsSymbol(type: IrType): IrFunctionSymbol
open fun IrExpression.transformArrayEqualsArgument(type: IrType, irBuilder: IrBlockBodyBuilder): IrExpression = this
fun generatedEquals(irBuilder: IrBlockBodyBuilder, type: IrType, arg1: IrExpression, arg2: IrExpression): IrExpression = fun generatedEquals(irBuilder: IrBlockBodyBuilder, type: IrType, arg1: IrExpression, arg2: IrExpression): IrExpression =
if (type.isArray() || type.isPrimitiveArray()) { if (type.isArray() || type.isPrimitiveArray() || type.isUnsignedArray()) {
val requiredSymbol = getArrayContentEqualsSymbol(type) val requiredSymbol = getArrayContentEqualsSymbol(type)
val lhs = arg1.transformArrayEqualsArgument(type, irBuilder)
val rhs = arg2.transformArrayEqualsArgument(type, irBuilder)
irBuilder.irCall( irBuilder.irCall(
requiredSymbol requiredSymbol
).apply { ).apply {
if (requiredSymbol.owner.extensionReceiverParameter != null) { if (requiredSymbol.owner.extensionReceiverParameter != null) {
extensionReceiver = arg1 extensionReceiver = lhs
putValueArgument(0, arg2) putValueArgument(0, rhs)
} else { } else {
putValueArgument(0, arg1) putValueArgument(0, lhs)
putValueArgument(1, arg2) putValueArgument(1, rhs)
} }
} }
} else } else
@@ -220,7 +227,7 @@ class AnnotationImplementationMemberGenerator(
irClass: IrClass, irClass: IrClass,
val nameForToString: String, val nameForToString: String,
forbidDirectFieldAccess: Boolean, forbidDirectFieldAccess: Boolean,
val selectEquals: IrBlockBodyBuilder.(IrType, IrExpression, IrExpression) -> IrExpression, val selectEquals: IrBlockBodyBuilder.(IrType, IrExpression, IrExpression) -> IrExpression
) : LoweringDataClassMemberGenerator(backendContext, irClass, ANNOTATION_IMPLEMENTATION, forbidDirectFieldAccess) { ) : LoweringDataClassMemberGenerator(backendContext, irClass, ANNOTATION_IMPLEMENTATION, forbidDirectFieldAccess) {
override fun IrClass.classNameForToString(): String = nameForToString override fun IrClass.classNameForToString(): String = nameForToString
@@ -263,3 +270,4 @@ class AnnotationImplementationMemberGenerator(
} }
} }
} }
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isArray import org.jetbrains.kotlin.ir.types.isArray
import org.jetbrains.kotlin.ir.util.isAnnotationClass import org.jetbrains.kotlin.ir.util.isAnnotationClass
import org.jetbrains.kotlin.ir.util.isPrimitiveArray import org.jetbrains.kotlin.ir.util.isPrimitiveArray
import org.jetbrains.kotlin.ir.util.isUnsignedArray
// JS PIR (and IC) requires DeclarationTransformer instead of FileLoweringPass // JS PIR (and IC) requires DeclarationTransformer instead of FileLoweringPass
@@ -53,7 +54,7 @@ class JsAnnotationImplementationTransformer(val jsContext: JsIrBackendContext) :
override fun getArrayContentEqualsSymbol(type: IrType) = override fun getArrayContentEqualsSymbol(type: IrType) =
when { when {
type.isPrimitiveArray() -> arraysContentEquals[type] type.isPrimitiveArray() || type.isUnsignedArray() -> arraysContentEquals[type]
else -> arraysContentEquals.entries.singleOrNull { (k, _) -> k.isArray() }?.value else -> arraysContentEquals.entries.singleOrNull { (k, _) -> k.isArray() }?.value
} ?: compilationException("Can't find an Arrays.contentEquals method for array type", type) } ?: compilationException("Can't find an Arrays.contentEquals method for array type", type)
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.isInPublicInlineScope import org.jetbrains.kotlin.backend.jvm.ir.isInPublicInlineScope
import org.jetbrains.kotlin.backend.jvm.ir.javaClassReference import org.jetbrains.kotlin.backend.jvm.ir.javaClassReference
import org.jetbrains.kotlin.backend.jvm.unboxInlineClass
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
@@ -134,8 +135,22 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext,
return block return block
} }
// There's no specialized Array.equals for unsigned arrays (as this is a Java function), so we force compiler not to box
// result of property getter call
override fun IrExpression.transformArrayEqualsArgument(type: IrType, irBuilder: IrBlockBodyBuilder): IrExpression =
if (!type.isUnsignedArray()) this
else irBuilder.irCall(jvmContext.ir.symbols.unsafeCoerceIntrinsic).apply {
putTypeArgument(0, type)
putTypeArgument(1, type.unboxInlineClass())
putValueArgument(0, this@transformArrayEqualsArgument)
}
override fun getArrayContentEqualsSymbol(type: IrType): IrFunctionSymbol { override fun getArrayContentEqualsSymbol(type: IrType): IrFunctionSymbol {
val targetType = if (type.isPrimitiveArray()) type else jvmContext.ir.symbols.arrayOfAnyNType val targetType = when {
type.isPrimitiveArray() -> type
type.isUnsignedArray() -> type.unboxInlineClass()
else -> jvmContext.ir.symbols.arrayOfAnyNType
}
val requiredSymbol = jvmContext.ir.symbols.arraysClass.owner.findDeclaration<IrFunction> { val requiredSymbol = jvmContext.ir.symbols.arraysClass.owner.findDeclaration<IrFunction> {
it.name.asString() == "equals" && it.valueParameters.size == 2 && it.valueParameters.first().type == targetType it.name.asString() == "equals" && it.valueParameters.size == 2 && it.valueParameters.first().type == targetType
} }
@@ -0,0 +1,15 @@
// IGNORE_BACKEND: JVM
// IGNORE_BACKEND: WASM
// DONT_TARGET_EXACT_BACKEND: JS
// WITH_STDLIB
// !LANGUAGE: +InstantiationOfAnnotationClasses
annotation class AnnotationWithSignedArray(val array: IntArray)
annotation class AnnotationWithUnsignedArray(val array: UIntArray)
fun box(): String {
if (!(AnnotationWithSignedArray(intArrayOf()) == AnnotationWithSignedArray(intArrayOf()))) return "Fail signed"
if (!(AnnotationWithUnsignedArray(uintArrayOf()) == AnnotationWithUnsignedArray(uintArrayOf()))) return "Fail unsigned"
return "OK"
}
@@ -456,6 +456,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/annotations/instances/annotationToString.kt"); runTest("compiler/testData/codegen/box/annotations/instances/annotationToString.kt");
} }
@Test
@TestMetadata("annotationsUnsignedTypes.kt")
public void testAnnotationsUnsignedTypes() throws Exception {
runTest("compiler/testData/codegen/box/annotations/instances/annotationsUnsignedTypes.kt");
}
@Test @Test
@TestMetadata("inInlineFunction.kt") @TestMetadata("inInlineFunction.kt")
public void testInInlineFunction() throws Exception { public void testInInlineFunction() throws Exception {
@@ -492,6 +492,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/annotations/instances/annotationType.kt"); runTest("compiler/testData/codegen/box/annotations/instances/annotationType.kt");
} }
@Test
@TestMetadata("annotationsUnsignedTypes.kt")
public void testAnnotationsUnsignedTypes() throws Exception {
runTest("compiler/testData/codegen/box/annotations/instances/annotationsUnsignedTypes.kt");
}
@Test @Test
@TestMetadata("inInlineFunction.kt") @TestMetadata("inInlineFunction.kt")
public void testInInlineFunction() throws Exception { public void testInInlineFunction() throws Exception {
@@ -395,6 +395,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/annotations/instances/annotationToString.kt"); runTest("compiler/testData/codegen/box/annotations/instances/annotationToString.kt");
} }
@TestMetadata("annotationsUnsignedTypes.kt")
public void ignoreAnnotationsUnsignedTypes() throws Exception {
runTest("compiler/testData/codegen/box/annotations/instances/annotationsUnsignedTypes.kt");
}
@TestMetadata("inInlineFunction.kt") @TestMetadata("inInlineFunction.kt")
public void ignoreInInlineFunction() throws Exception { public void ignoreInInlineFunction() throws Exception {
runTest("compiler/testData/codegen/box/annotations/instances/inInlineFunction.kt"); runTest("compiler/testData/codegen/box/annotations/instances/inInlineFunction.kt");
@@ -120,6 +120,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/annotations/instances/annotationToString.kt"); runTest("compiler/testData/codegen/box/annotations/instances/annotationToString.kt");
} }
@Test
@TestMetadata("annotationsUnsignedTypes.kt")
public void testAnnotationsUnsignedTypes() throws Exception {
runTest("compiler/testData/codegen/box/annotations/instances/annotationsUnsignedTypes.kt");
}
@Test @Test
@TestMetadata("inInlineFunction.kt") @TestMetadata("inInlineFunction.kt")
public void testInInlineFunction() throws Exception { public void testInInlineFunction() throws Exception {
@@ -123,6 +123,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/annotations/instances/annotationToString.kt"); runTest("compiler/testData/codegen/box/annotations/instances/annotationToString.kt");
} }
@TestMetadata("annotationsUnsignedTypes.kt")
public void testAnnotationsUnsignedTypes() throws Exception {
runTest("compiler/testData/codegen/box/annotations/instances/annotationsUnsignedTypes.kt");
}
@TestMetadata("inInlineFunction.kt") @TestMetadata("inInlineFunction.kt")
public void testInInlineFunction() throws Exception { public void testInInlineFunction() throws Exception {
runTest("compiler/testData/codegen/box/annotations/instances/inInlineFunction.kt"); runTest("compiler/testData/codegen/box/annotations/instances/inInlineFunction.kt");
@@ -26,7 +26,7 @@ internal class NativeAnnotationImplementationTransformer(context: Context, irFil
override fun getArrayContentEqualsSymbol(type: IrType) = override fun getArrayContentEqualsSymbol(type: IrType) =
when { when {
type.isPrimitiveArray() -> arrayContentEqualsMap[type] type.isPrimitiveArray() || type.isUnsignedArray() -> arrayContentEqualsMap[type]
else -> arrayContentEqualsMap.entries.singleOrNull { (k, _) -> k.isArray() }?.value else -> arrayContentEqualsMap.entries.singleOrNull { (k, _) -> k.isArray() }?.value
} ?: error("Can't find an Arrays.contentEquals method for array type ${type.render()}") } ?: error("Can't find an Arrays.contentEquals method for array type ${type.render()}")
@@ -132,6 +132,12 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
runTest("compiler/testData/codegen/box/annotations/instances/annotationToString.kt"); runTest("compiler/testData/codegen/box/annotations/instances/annotationToString.kt");
} }
@Test
@TestMetadata("annotationsUnsignedTypes.kt")
public void testAnnotationsUnsignedTypes() throws Exception {
runTest("compiler/testData/codegen/box/annotations/instances/annotationsUnsignedTypes.kt");
}
@Test @Test
@TestMetadata("inInlineFunction.kt") @TestMetadata("inInlineFunction.kt")
public void testInInlineFunction() throws Exception { public void testInInlineFunction() throws Exception {