diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java index d04eae38ee9..f8022aa60d0 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java @@ -3835,6 +3835,12 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/inlineClasses/isCheckForInlineClass.kt"); } + @Test + @TestMetadata("kt33722.kt") + public void testKt33722() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/inlineClasses/kt33722.kt"); + } + @Test @TestMetadata("mangledInlineClassInterfaceImplementation.kt") public void testMangledInlineClassInterfaceImplementation() throws Exception { diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt index d3a36aaf649..27f0ba11b9e 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrValueSymbol import org.jetbrains.kotlin.ir.transformStatement import org.jetbrains.kotlin.ir.types.* @@ -344,32 +345,49 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass coerceInlineClasses(arg, expression.symbol.owner.dispatchReceiverParameter!!.type, expression.type) } // Specialize calls to equals when the left argument is a value of inline class type. - expression.isSpecializedInlineClassEqEq -> { + expression.isSpecializedInlineClassEqEq || expression.isSpecializedInlineClassEquals -> { expression.transformChildrenVoid() + val leftOp: IrExpression + val rightOp: IrExpression + if (expression.isSpecializedInlineClassEqEq) { + leftOp = expression.getValueArgument(0)!! + rightOp = expression.getValueArgument(1)!! + } else { + leftOp = expression.dispatchReceiver!! + rightOp = expression.getValueArgument(0)!! + } context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset) - .specializeEqualsCall(expression.getValueArgument(0)!!, expression.getValueArgument(1)!!) + .specializeEqualsCall(leftOp, rightOp) ?: expression } - else -> super.visitCall(expression) } + private val IrCall.isSpecializedInlineClassEquals: Boolean + get() { + return isSpecializedInlineClassEqualityCheck { symbol.owner.isEquals() } + } + private val IrCall.isSpecializedInlineClassEqEq: Boolean get() { // Note that reference equality (x === y) is not allowed on values of inline class type, // so it is enough to check for eqeq. - if (symbol != context.irBuiltIns.eqeqSymbol) - return false - - val leftClass = getValueArgument(0)?.type?.classOrNull?.owner?.takeIf { it.isSingleFieldValueClass } - ?: return false - - // Before version 1.4, we cannot rely on the Result.equals-impl0 method - return (leftClass.fqNameWhenAvailable != StandardNames.RESULT_FQ_NAME) || - context.state.languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_4 + return isSpecializedInlineClassEqualityCheck { symbol == context.irBuiltIns.eqeqSymbol } } + private inline fun IrCall.isSpecializedInlineClassEqualityCheck(calleePredicate: (IrSimpleFunctionSymbol) -> Boolean): Boolean { + + if (!calleePredicate(symbol)) return false + + val leftClass = getValueArgument(0)?.type?.classOrNull?.owner?.takeIf { it.isSingleFieldValueClass } + ?: return false + + // Before version 1.4, we cannot rely on the Result.equals-impl0 method + return (leftClass.fqNameWhenAvailable != StandardNames.RESULT_FQ_NAME) || + context.state.languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_4 + } + override fun visitGetField(expression: IrGetField): IrExpression { val field = expression.symbol.owner val parent = field.parent diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/equalsIsCalledByInlineClass.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/equalsIsCalledByInlineClass.kt index ace25a5efb9..4ba4b2fc61b 100644 --- a/compiler/testData/codegen/bytecodeText/inlineClasses/equalsIsCalledByInlineClass.kt +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/equalsIsCalledByInlineClass.kt @@ -9,6 +9,10 @@ fun testZ(z: Z, a: Any?) = z.equals(a) fun testNZ(z: Z?) = z?.equals(z) // @TestKt.class: +// JVM_IR_TEMPLATES // 0 INVOKESTATIC Z\$Erased\.equals // 0 INVOKESTATIC Z\-Erased\.equals -// 3 INVOKESTATIC Z\.equals-impl \(ILjava/lang/Object;\)Z \ No newline at end of file +// 1 INVOKESTATIC Z\.equals-impl0 \(II\)Z +// 1 INVOKESTATIC Z\.equals-impl \(ILjava/lang/Object;\)Z +// 1 INVOKEVIRTUAL Z.equals +// 0 INVOKEVIRTUAL Z.unbox-impl \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/kt33722.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/kt33722.kt new file mode 100644 index 00000000000..52c27dd77ce --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/kt33722.kt @@ -0,0 +1,17 @@ +// WITH_STDLIB +// TARGET_BACKEND: JVM_IR + +fun foo() { + val result = Result.success("yes!") + val other = Result.success("nope") + + result == other + result != other + + result.equals(other) + !result.equals(other) +} + +// CHECK_BYTECODE_TEXT +// 0 INVOKESTATIC kotlin/Result.box-impl +// 4 INVOKESTATIC kotlin/Result.equals-impl0 \ No newline at end of file diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java index baa71aa5c39..fdbd5777719 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java @@ -3835,6 +3835,12 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/inlineClasses/isCheckForInlineClass.kt"); } + @Test + @TestMetadata("kt33722.kt") + public void testKt33722() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/inlineClasses/kt33722.kt"); + } + @Test @TestMetadata("mangledInlineClassInterfaceImplementation.kt") public void testMangledInlineClassInterfaceImplementation() throws Exception {