Get rid of redundant boxing when comparing inline class instances
^KT-33722: Fixed
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 d04eae3..f8022aa 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
@@ -3836,6 +3836,12 @@
}
@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 {
runTest("compiler/testData/codegen/bytecodeText/inlineClasses/mangledInlineClassInterfaceImplementation.kt");
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 d3a36aa..27f0ba1 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.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 @@
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 ace25a5..4ba4b2f 100644
--- a/compiler/testData/codegen/bytecodeText/inlineClasses/equalsIsCalledByInlineClass.kt
+++ b/compiler/testData/codegen/bytecodeText/inlineClasses/equalsIsCalledByInlineClass.kt
@@ -9,6 +9,10 @@
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 0000000..52c27dd
--- /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 baa71aa..fdbd577 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
@@ -3836,6 +3836,12 @@
}
@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 {
runTest("compiler/testData/codegen/bytecodeText/inlineClasses/mangledInlineClassInterfaceImplementation.kt");