From 6f954462d6a3fa5cd139679e61c911dbf19eb658 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Mon, 6 Sep 2021 19:29:08 +0300 Subject: [PATCH] Implement proper contract for generated java.lang.Annotation.hashCode() #KT-48606 Fixed --- .../kotlin/fir/backend/IrBuiltInsOverFir.kt | 5 + .../FirBlackBoxCodegenTestGenerated.java | 6 + .../AnnotationImplementationTransformer.kt | 69 ++++++++--- .../MethodsFromAnyGeneratorForLowerings.kt | 117 ++++++++---------- .../src/org/jetbrains/kotlin/ir/IrBuiltIns.kt | 1 + .../descriptors/IrBuiltInsOverDescriptors.kt | 10 +- .../ir/util/DataClassMembersGenerator.kt | 56 +++++---- .../annotations/instances/annotationEqHc.kt | 8 +- .../instances/annotationJvmHashCode.kt | 35 ++++++ .../IrBlackBoxCodegenTestGenerated.java | 6 + 10 files changed, 200 insertions(+), 113 deletions(-) create mode 100644 compiler/testData/codegen/box/annotations/instances/annotationJvmHashCode.kt diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt index a69bc209fdf..210bb9b376c 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt @@ -455,6 +455,11 @@ class IrBuiltInsOverFir( it.owner.name == OperatorNameConventions.TIMES && it.owner.valueParameters[0].type == intType } + override val intXorSymbol: IrSimpleFunctionSymbol + get() = intClass.functions.single { + it.owner.name == OperatorNameConventions.XOR && it.owner.valueParameters[0].type == intType + } + override val extensionToString: IrSimpleFunctionSymbol by lazy { findFunctions(kotlinPackage, Name.identifier("toString")).first { function -> function.owner.extensionReceiverParameter?.let { receiver -> receiver.type == anyNType } ?: false diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index fe475d51963..b8e51ea62f6 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -419,6 +419,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/annotations/instances/annotationInstancesEmptyDefault.kt"); } + @Test + @TestMetadata("annotationJvmHashCode.kt") + public void testAnnotationJvmHashCode() throws Exception { + runTest("compiler/testData/codegen/box/annotations/instances/annotationJvmHashCode.kt"); + } + @Test @TestMetadata("annotationToString.kt") public void testAnnotationToString() throws Exception { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt index b8364247caa..72daf291fe5 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt @@ -13,19 +13,19 @@ import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.IrConstructorCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrGetValue import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.isArray import org.jetbrains.kotlin.ir.types.isKClass import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @@ -183,18 +183,13 @@ open class AnnotationImplementationTransformer(val context: BackendContext, val @Suppress("UNUSED_VARIABLE") fun implementEqualsAndHashCode(annotationClass: IrClass, implClass: IrClass, originalProps: List, childProps: List) { val creator = MethodsFromAnyGeneratorForLowerings(context, implClass, ANNOTATION_IMPLEMENTATION) - val generator = - creator.LoweringDataClassMemberGenerator( - nameForToString = "@" + annotationClass.fqNameWhenAvailable!!.asString(), - typeForEquals = annotationClass.defaultType - ) { type, a, b -> - generatedEquals(this, type, a, b) - } + val generator = AnnotationImplementationMemberGenerator( + context, implClass, + nameForToString = "@" + annotationClass.fqNameWhenAvailable!!.asString(), + ) { type, a, b -> + generatedEquals(this, type, a, b) + } - // Manual implementation of equals is required for two reasons: - // 1. `other` should be casted to interface instead of implementation - // 2. Properties should be retrieved using getters without accessing backing fields - // (DataClassMembersGenerator typically tries to access fields) val eqFun = creator.createEqualsMethodDeclaration() generator.generateEqualsUsingGetters(eqFun, annotationClass.defaultType, originalProps) @@ -208,3 +203,49 @@ open class AnnotationImplementationTransformer(val context: BackendContext, val open fun implementPlatformSpecificParts(annotationClass: IrClass, implClass: IrClass) {} } +class AnnotationImplementationMemberGenerator( + backendContext: BackendContext, + irClass: IrClass, + val nameForToString: String, + val selectEquals: IrBlockBodyBuilder.(IrType, IrExpression, IrExpression) -> IrExpression, +) : LoweringDataClassMemberGenerator(backendContext, irClass, ANNOTATION_IMPLEMENTATION) { + + override fun IrClass.classNameForToString(): String = nameForToString + + // From https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Annotation.html#equals-java.lang.Object- + // --- + // The hash code of an annotation is the sum of the hash codes of its members (including those with default values), as defined below: + // The hash code of an annotation member is (127 times the hash code of the member-name as computed by String.hashCode()) XOR the hash code of the member-value + override fun IrBuilderWithScope.shiftResultOfHashCode(irResultVar: IrVariable): IrExpression = irGet(irResultVar) // no default (* 31) + + override fun getHashCodeOf(builder: IrBuilderWithScope, property: IrProperty, irValue: IrExpression): IrExpression = with(builder) { + val propertyValueHashCode = getHashCodeOf(property.backingField!!.type, irValue) + val propertyNameHashCode = getHashCodeOf(backendContext.irBuiltIns.stringType, irString(property.name.toString())) + val multiplied = irCallOp(context.irBuiltIns.intTimesSymbol, context.irBuiltIns.intType, propertyNameHashCode, irInt(127)) + return irCallOp(context.irBuiltIns.intXorSymbol, context.irBuiltIns.intType, multiplied, propertyValueHashCode) + } + + // Manual implementation of equals is required for two reasons: + // 1. `other` should be casted to interface instead of implementation + // 2. Properties should be retrieved using getters without accessing backing fields + // (DataClassMembersGenerator typically tries to access fields) + fun generateEqualsUsingGetters(equalsFun: IrSimpleFunction, typeForEquals: IrType, properties: List) = equalsFun.apply { + body = backendContext.createIrBuilder(symbol).irBlockBody { + val irType = typeForEquals + fun irOther() = irGet(valueParameters[0]) + fun irThis() = irGet(dispatchReceiverParameter!!) + fun IrProperty.get(receiver: IrExpression) = irCall(getter!!).apply { + dispatchReceiver = receiver + } + + +irIfThenReturnFalse(irNotIs(irOther(), irType)) + val otherWithCast = irTemporary(irAs(irOther(), irType), "other_with_cast") + for (property in properties) { + val arg1 = property.get(irThis()) + val arg2 = property.get(irGet(irType, otherWithCast.symbol)) + +irIfThenReturnFalse(irNot(selectEquals(property.getter?.returnType ?: property.backingField!!.type, arg1, arg2))) + } + +irReturnTrue() + } + } +} diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/MethodsFromAnyGeneratorForLowerings.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/MethodsFromAnyGeneratorForLowerings.kt index 94c5ad2ce3f..4ea853d74ab 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/MethodsFromAnyGeneratorForLowerings.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/MethodsFromAnyGeneratorForLowerings.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.backend.common.lower import org.jetbrains.kotlin.backend.common.BackendContext +import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.isHashCode import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor @@ -38,71 +39,6 @@ class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irCla addValueParameter("other", context.irBuiltIns.anyNType) } - inner class LoweringDataClassMemberGenerator( - val nameForToString: String, - val typeForEquals: IrType, - val selectEquals: IrBlockBodyBuilder.(IrType, IrExpression, IrExpression) -> IrExpression, - ) : - DataClassMembersGenerator( - IrGeneratorContextBase(context.irBuiltIns), - context.ir.symbols.externalSymbolTable, - irClass, - origin - ) { - - override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction { - error("Descriptor API shouldn't be used in lowerings") - } - - override fun generateSyntheticFunctionParameterDeclarations(irFunction: IrFunction) { - // no-op — irFunction from lowering should already have necessary parameters - } - - override fun getProperty(parameter: ValueParameterDescriptor?, irValueParameter: IrValueParameter?): IrProperty? { - error("Descriptor API shouldn't be used in lowerings") - } - - override fun transform(typeParameterDescriptor: TypeParameterDescriptor): IrType { - error("Descriptor API shouldn't be used in lowerings") - } - - override fun getHashCodeFunctionInfo(type: IrType): HashCodeFunctionInfo { - val symbol = if (type.isArray() || type.isPrimitiveArray()) { - context.irBuiltIns.dataClassArrayMemberHashCodeSymbol - } else { - type.classOrNull?.functions?.singleOrNull { it.owner.isHashCode() } ?: - context.irBuiltIns.anyClass.functions.single { it.owner.name.asString() == "hashCode" } - } - return object : HashCodeFunctionInfo { - override val symbol: IrSimpleFunctionSymbol = symbol - - override fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression<*>) {} - } - } - - override fun IrClass.classNameForToString(): String = nameForToString - - fun generateEqualsUsingGetters(equalsFun: IrSimpleFunction, typeForEquals: IrType, properties: List) = equalsFun.apply { - body = this@MethodsFromAnyGeneratorForLowerings.context.createIrBuilder(symbol).irBlockBody { - val irType = typeForEquals - fun irOther() = irGet(valueParameters[0]) - fun irThis() = irGet(dispatchReceiverParameter!!) - fun IrProperty.get(receiver: IrExpression) = irCall(getter!!).apply { - dispatchReceiver = receiver - } - - +irIfThenReturnFalse(irNotIs(irOther(), irType)) - val otherWithCast = irTemporary(irAs(irOther(), irType), "other_with_cast") - for (property in properties) { - val arg1 = property.get(irThis()) - val arg2 = property.get(irGet(irType, otherWithCast.symbol)) - +irIfThenReturnFalse(irNot(selectEquals(property.getter?.returnType ?: property.backingField!!.type, arg1, arg2))) - } - +irReturnTrue() - } - } - } - companion object { fun IrFunction.isToString(): Boolean = name.asString() == "toString" && extensionReceiverParameter == null && valueParameters.isEmpty() @@ -118,6 +54,53 @@ class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irCla fun IrClass.collectOverridenSymbols(predicate: (IrFunction) -> Boolean): List = superTypes.mapNotNull { it.getClass()?.functions?.singleOrNull(predicate)?.symbol } - } -} \ No newline at end of file +} + +open class LoweringDataClassMemberGenerator( + val backendContext: BackendContext, + irClass: IrClass, + origin: IrDeclarationOrigin +) : + DataClassMembersGenerator( + IrGeneratorContextBase(backendContext.irBuiltIns), + backendContext.ir.symbols.externalSymbolTable, + irClass, + origin + ) { + + override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction { + error("Descriptor API shouldn't be used in lowerings") + } + + override fun generateSyntheticFunctionParameterDeclarations(irFunction: IrFunction) { + // no-op — irFunction from lowering should already have necessary parameters + } + + override fun getProperty(parameter: ValueParameterDescriptor?, irValueParameter: IrValueParameter?): IrProperty? { + error("Descriptor API shouldn't be used in lowerings") + } + + override fun transform(typeParameterDescriptor: TypeParameterDescriptor): IrType { + error("Descriptor API shouldn't be used in lowerings") + } + + override fun getHashCodeFunctionInfo(type: IrType): HashCodeFunctionInfo { + val symbol = if (type.isArray() || type.isPrimitiveArray()) { + context.irBuiltIns.dataClassArrayMemberHashCodeSymbol + } else { + type.classOrNull?.functions?.singleOrNull { it.owner.isHashCode() } + ?: context.irBuiltIns.anyClass.functions.single { it.owner.name.asString() == "hashCode" } + } + return object : HashCodeFunctionInfo { + override val symbol: IrSimpleFunctionSymbol = symbol + + override fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression<*>) {} + } + } +} + + + + + diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt index f58083fd274..8db6bb46eb4 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt @@ -139,6 +139,7 @@ abstract class IrBuiltIns { abstract val intPlusSymbol: IrSimpleFunctionSymbol abstract val intTimesSymbol: IrSimpleFunctionSymbol + abstract val intXorSymbol: IrSimpleFunctionSymbol abstract val extensionToString: IrSimpleFunctionSymbol abstract val stringPlus: IrSimpleFunctionSymbol diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltInsOverDescriptors.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltInsOverDescriptors.kt index 1db0b78a3bd..c4620569693 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltInsOverDescriptors.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltInsOverDescriptors.kt @@ -32,10 +32,7 @@ import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeBuilder import org.jetbrains.kotlin.ir.types.impl.buildSimpleType import org.jetbrains.kotlin.ir.types.impl.originalKotlinType -import org.jetbrains.kotlin.ir.util.SymbolTable -import org.jetbrains.kotlin.ir.util.TypeTranslator -import org.jetbrains.kotlin.ir.util.functions -import org.jetbrains.kotlin.ir.util.referenceClassifier +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.scopes.MemberScope @@ -387,6 +384,11 @@ class IrBuiltInsOverDescriptors( KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, int) }.let { symbolTable.referenceSimpleFunction(it) } + override val intXorSymbol: IrSimpleFunctionSymbol = + builtIns.int.unsubstitutedMemberScope.findFirstFunction("xor") { + KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, int) + }.let { symbolTable.referenceSimpleFunction(it) } + override val intPlusSymbol: IrSimpleFunctionSymbol = builtIns.int.unsubstitutedMemberScope.findFirstFunction("plus") { KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, int) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt index b90e5e16abb..8950efe87aa 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt @@ -16,7 +16,9 @@ import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.expressions.mapTypeParameters import org.jetbrains.kotlin.ir.expressions.mapValueParameters -import org.jetbrains.kotlin.ir.symbols.* +import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol +import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classifierOrNull @@ -160,7 +162,7 @@ abstract class DataClassMembersGenerator( +irResultVar for (property in properties.drop(1)) { - val shiftedResult = irCallOp(context.irBuiltIns.intTimesSymbol, irIntType, irGet(irResultVar), irInt(31)) + val shiftedResult = shiftResultOfHashCode(irResultVar) val irRhs = irCallOp(context.irBuiltIns.intPlusSymbol, irIntType, shiftedResult, getHashCodeOfProperty(property)) +irSet(irResultVar.symbol, irRhs) } @@ -175,30 +177,10 @@ abstract class DataClassMembersGenerator( context.irBuiltIns.intType, irGetProperty(irThis(), property), irInt(0), - getHashCodeOf(property, irGetProperty(irThis(), property)) + getHashCodeOf(this, property, irGetProperty(irThis(), property)) ) else -> - getHashCodeOf(property, irGetProperty(irThis(), property)) - } - } - - private fun getHashCodeOf(property: IrProperty, irValue: IrExpression): IrExpression { - val hashCodeFunctionInfo = getHashCodeFunctionInfo(property.backingField!!.type) - val hashCodeFunctionSymbol = hashCodeFunctionInfo.symbol - - val hasDispatchReceiver = hashCodeFunctionSymbol.descriptor.dispatchReceiverParameter != null - return irCall( - hashCodeFunctionSymbol, - context.irBuiltIns.intType, - valueArgumentsCount = if (hasDispatchReceiver) 0 else 1, - typeArgumentsCount = 0 - ).apply { - if (hasDispatchReceiver) { - dispatchReceiver = irValue - } else { - putValueArgument(0, irValue) - } - hashCodeFunctionInfo.commitSubstituted(this) + getHashCodeOf(this, property, irGetProperty(irThis(), property)) } } @@ -230,6 +212,32 @@ abstract class DataClassMembersGenerator( } } + protected open fun IrBuilderWithScope.shiftResultOfHashCode(irResultVar: IrVariable): IrExpression = + irCallOp(context.irBuiltIns.intTimesSymbol, context.irBuiltIns.intType, irGet(irResultVar), irInt(31)) + + protected open fun getHashCodeOf(builder: IrBuilderWithScope, property: IrProperty, irValue: IrExpression) = + builder.getHashCodeOf(property.backingField!!.type, irValue) + + protected fun IrBuilderWithScope.getHashCodeOf(type: IrType, irValue: IrExpression): IrExpression { + val hashCodeFunctionInfo = getHashCodeFunctionInfo(type) + val hashCodeFunctionSymbol = hashCodeFunctionInfo.symbol + val hasDispatchReceiver = hashCodeFunctionSymbol.descriptor.dispatchReceiverParameter != null + return irCall( + hashCodeFunctionSymbol, + context.irBuiltIns.intType, + valueArgumentsCount = if (hasDispatchReceiver) 0 else 1, + typeArgumentsCount = 0 + ).apply { + if (hasDispatchReceiver) { + dispatchReceiver = irValue + } else { + putValueArgument(0, irValue) + } + hashCodeFunctionInfo.commitSubstituted(this) + } + } + + fun getIrProperty(property: PropertyDescriptor): IrProperty = irPropertiesByDescriptor[property] ?: throw AssertionError("Class: ${irClass.descriptor}: unexpected property descriptor: $property") diff --git a/compiler/testData/codegen/box/annotations/instances/annotationEqHc.kt b/compiler/testData/codegen/box/annotations/instances/annotationEqHc.kt index 8174d900980..4d2da7a481c 100644 --- a/compiler/testData/codegen/box/annotations/instances/annotationEqHc.kt +++ b/compiler/testData/codegen/box/annotations/instances/annotationEqHc.kt @@ -20,14 +20,14 @@ annotation class Foo( val bar: Bar ) -data class BarLike(val i:Int, val s: String, val f: Float) +fun makeHC(name: String, value: Any) = (127 * name.hashCode()) xor value.hashCode() fun box(): String { val foo1 = Foo(42, "foo", arrayOf("a", "b"), intArrayOf(1,2), Bar::class, Bar(10, "bar", Float.NaN)) val foo2 = Foo(42, "foo", arrayOf("a", "b"), intArrayOf(1,2), Bar::class, Bar(10, "bar", Float.NaN)) if (foo1 != foo2) return "Failed equals ${foo1.toString()} ${foo2.toString()}" - val barlike = BarLike(10, "bar", Float.NaN) - if (barlike.hashCode() != foo1.bar.hashCode()) return "Failed HC1" - if (barlike.hashCode() != foo2.bar.hashCode()) return "Failed HC2" + val barlike = makeHC("i", 10) + makeHC("s", "bar") + makeHC("f", Float.NaN) + if (barlike != foo1.bar.hashCode()) return "Failed HC1" + if (barlike != foo2.bar.hashCode()) return "Failed HC2" return "OK" } \ No newline at end of file diff --git a/compiler/testData/codegen/box/annotations/instances/annotationJvmHashCode.kt b/compiler/testData/codegen/box/annotations/instances/annotationJvmHashCode.kt new file mode 100644 index 00000000000..47e34f44517 --- /dev/null +++ b/compiler/testData/codegen/box/annotations/instances/annotationJvmHashCode.kt @@ -0,0 +1,35 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM_IR + +// WITH_RUNTIME +// !LANGUAGE: +InstantiationOfAnnotationClasses + +import kotlin.reflect.KClass +import kotlin.test.assertEquals +import kotlin.test.assertTrue as assert + +annotation class ZeroArg() + +annotation class OneArg(val arg: String) + +annotation class ManyArg(val i: Int, val o: OneArg, val z: Boolean, val k: KClass<*>, val e: IntArray) + +@ZeroArg +@OneArg("a") +@ManyArg(42, OneArg("b"), true, OneArg::class, intArrayOf(1, 2, 3)) +class Target + +fun box(): String { + val reflectiveZero = Target::class.java.getAnnotation(ZeroArg::class.java) + val reflectiveOne = Target::class.java.getAnnotation(OneArg::class.java) + val reflectiveMany = Target::class.java.getAnnotation(ManyArg::class.java) + + val createdZero = ZeroArg() + val createdOne = OneArg("a") + val createdMany = ManyArg(42, OneArg("b"), true, OneArg::class, intArrayOf(1, 2, 3)) + + assertEquals(reflectiveZero.hashCode(), createdZero.hashCode(), "zero") + assertEquals(reflectiveOne.hashCode(), createdOne.hashCode(), "one") + assertEquals(reflectiveMany.hashCode(), createdMany.hashCode(), "many") + return "OK" +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 4713530e414..827e888932a 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -419,6 +419,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/annotations/instances/annotationInstancesEmptyDefault.kt"); } + @Test + @TestMetadata("annotationJvmHashCode.kt") + public void testAnnotationJvmHashCode() throws Exception { + runTest("compiler/testData/codegen/box/annotations/instances/annotationJvmHashCode.kt"); + } + @Test @TestMetadata("annotationToString.kt") public void testAnnotationToString() throws Exception {