diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java index 925f8e7f919..522ef5c5373 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java @@ -140,6 +140,10 @@ public class AsmUtil { return primitiveTypeByBoxedType.get(boxedType); } + public static boolean isBoxedTypeOf(@NotNull Type boxedType, @NotNull Type unboxedType) { + return unboxPrimitiveTypeOrNull(boxedType) == unboxedType; + } + public static boolean isIntPrimitive(Type type) { return type == Type.INT_TYPE || type == Type.SHORT_TYPE || type == Type.BYTE_TYPE || type == Type.CHAR_TYPE; } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/BoxedVsPrimitiveBranchedValues.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/BoxedVsPrimitiveBranchedValues.kt index 6b56fd6ea26..c2d8087039d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/BoxedVsPrimitiveBranchedValues.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/BoxedVsPrimitiveBranchedValues.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen import com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter @@ -112,3 +113,130 @@ class PrimitiveToSafeCallEquality( } } + +class BoxedToPrimitiveEquality private constructor( + leftBoxed: StackValue, + rightPrimitive: StackValue, + primitiveType: Type +) : BranchedValue(leftBoxed, rightPrimitive, primitiveType, Opcodes.IFNE) { + private val boxedType = arg1.type + + override fun patchOpcode(opcode: Int, v: InstructionAdapter): Int = + NumberCompare.patchOpcode(opcode, v, KtTokens.EQEQ, operandType) + + override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) { + if (jumpIfFalse) { + jumpIfFalse(v, jumpLabel) + } + else { + jumpIfTrue(v, jumpLabel) + } + } + + private fun jumpIfTrue(v: InstructionAdapter, jumpLabel: Label) { + if (arg1.canHaveSideEffects() || arg2!!.canHaveSideEffects()) { + jumpIfTrueWithPossibleSideEffects(v, jumpLabel) + return + } + + val notNullLabel = Label() + val endLabel = Label() + arg1.put(boxedType, v) + AsmUtil.dup(v, boxedType) + v.ifnonnull(notNullLabel) + + AsmUtil.pop(v, boxedType) + v.goTo(endLabel) + + v.mark(notNullLabel) + coerce(boxedType, operandType, v) + arg2.put(operandType, v) + v.visitJumpInsn(patchOpcode(negatedOperations[opcode]!!, v), jumpLabel) + + v.mark(endLabel) + } + + private fun jumpIfTrueWithPossibleSideEffects(v: InstructionAdapter, jumpLabel: Label) { + val notNullLabel = Label() + val endLabel = Label() + + arg1.put(boxedType, v) + arg2!!.put(operandType, v) + AsmUtil.swap(v, operandType, boxedType) + AsmUtil.dup(v, boxedType) + v.ifnonnull(notNullLabel) + + AsmUtil.pop(v, boxedType) + AsmUtil.pop(v, operandType) + v.goTo(endLabel) + + v.mark(notNullLabel) + coerce(boxedType, operandType, v) + v.visitJumpInsn(patchOpcode(negatedOperations[opcode]!!, v), jumpLabel) + + v.mark(endLabel) + + } + + private fun jumpIfFalse(v: InstructionAdapter, jumpLabel: Label) { + if (arg1.canHaveSideEffects() || arg2!!.canHaveSideEffects()) { + jumpIfFalseWithPossibleSideEffects(v, jumpLabel) + return + } + + val notNullLabel = Label() + arg1.put(boxedType, v) + AsmUtil.dup(v, boxedType) + v.ifnonnull(notNullLabel) + + AsmUtil.pop(v, boxedType) + v.goTo(jumpLabel) + + v.mark(notNullLabel) + coerce(boxedType, operandType, v) + arg2.put(operandType, v) + v.visitJumpInsn(patchOpcode(opcode, v), jumpLabel) + } + + private fun jumpIfFalseWithPossibleSideEffects(v: InstructionAdapter, jumpLabel: Label) { + val notNullLabel = Label() + arg1.put(boxedType, v) + arg2!!.put(operandType, v) + AsmUtil.swap(v, operandType, boxedType) + AsmUtil.dup(v, boxedType) + v.ifnonnull(notNullLabel) + + AsmUtil.pop(v, boxedType) + AsmUtil.pop(v, operandType) + v.goTo(jumpLabel) + + v.mark(notNullLabel) + coerce(boxedType, operandType, v) + v.visitJumpInsn(patchOpcode(opcode, v), jumpLabel) + } + + companion object { + @JvmStatic + fun create(opToken: IElementType, leftBoxed: StackValue, rightPrimitive: StackValue, primitiveType: Type): BranchedValue = + if (!isApplicable(opToken, primitiveType)) + throw IllegalArgumentException("Not applicable for $opToken, $primitiveType") + else when (opToken) { + KtTokens.EQEQ -> BoxedToPrimitiveEquality(leftBoxed, rightPrimitive, primitiveType) + KtTokens.EXCLEQ -> Invert(BoxedToPrimitiveEquality(leftBoxed, rightPrimitive, primitiveType)) + else -> throw AssertionError("Unexpected opToken: $opToken") + } + + @JvmStatic + fun isApplicable(opToken: IElementType, primitiveType: Type) = + (opToken == KtTokens.EQEQ || + opToken == KtTokens.EXCLEQ + ) && + (primitiveType == Type.BOOLEAN_TYPE || + primitiveType == Type.CHAR_TYPE || + primitiveType == Type.BYTE_TYPE || + primitiveType == Type.SHORT_TYPE || + primitiveType == Type.INT_TYPE || + primitiveType == Type.LONG_TYPE + ) + } +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index daf403062c6..c962062de7d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -39,12 +39,12 @@ import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenForLambda; import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; import org.jetbrains.kotlin.codegen.coroutines.ResolvedCallWithRealDescriptor; import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension; -import org.jetbrains.kotlin.codegen.range.forLoop.ForLoopGenerator; import org.jetbrains.kotlin.codegen.inline.*; import org.jetbrains.kotlin.codegen.intrinsics.*; import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt; import org.jetbrains.kotlin.codegen.range.RangeValue; import org.jetbrains.kotlin.codegen.range.RangeValuesKt; +import org.jetbrains.kotlin.codegen.range.forLoop.ForLoopGenerator; import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter; import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter; import org.jetbrains.kotlin.codegen.state.GenerationState; @@ -2895,6 +2895,10 @@ public class ExpressionCodegen extends KtVisitor impleme return genCmpPrimitiveToSafeCall(left, leftType, (KtSafeQualifiedExpression) right, opToken); } + if (isBoxedTypeOf(leftType, rightType) && BoxedToPrimitiveEquality.isApplicable(opToken, rightType)) { + return BoxedToPrimitiveEquality.create(opToken, genLazy(left, leftType), genLazy(right, rightType), rightType); + } + if (isPrimitive(leftType) != isPrimitive(rightType)) { leftType = boxType(leftType); rightType = boxType(rightType); diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/boxedEqPrimitiveEvaluationOrder.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/boxedEqPrimitiveEvaluationOrder.kt new file mode 100644 index 00000000000..c8d43de6623 --- /dev/null +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/boxedEqPrimitiveEvaluationOrder.kt @@ -0,0 +1,26 @@ +var order: String = "" + +fun a(i: Int): Int? { + order += "a" + return i +} + +fun b(i: Int): Int { + order += "b" + return i +} + +inline fun evaluateAndCheckOrder(marker: String, expectedValue: Boolean, expectedOrder: String, expr: () -> Boolean) { + order = "" + val actualValue = expr() + if (actualValue != expectedValue) throw AssertionError("$marker: Expected: $expectedValue, actual: $actualValue") + if (order != expectedOrder) throw AssertionError("$marker, order: Expected: '$expectedOrder', actual: '$order'") +} + +fun box(): String { + evaluateAndCheckOrder("1", true, "ab") { a(1) == b(1) } + evaluateAndCheckOrder("2", true, "ab") { a(1) != b(2) } + evaluateAndCheckOrder("3", true, "ab") { !(a(1) == b(2)) } + evaluateAndCheckOrder("4", true, "ab") { !(a(1) != b(1)) } + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveBoolean.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveBoolean.kt new file mode 100644 index 00000000000..0194c7abf96 --- /dev/null +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveBoolean.kt @@ -0,0 +1,41 @@ +// Auto-generated by GeneratePrimitiveEqualityWithNullableTestData. Do not edit! + +val nx: Boolean? = true +val nn: Boolean? = null +val x: Boolean = true +val y: Boolean = false + +fun box(): String { + val ax: Boolean? = true + val an: Boolean? = null + val bx: Boolean = true + val by: Boolean = false + + return when { + nx != true -> "Fail 0" + nx == false -> "Fail 1" + !(nx == true) -> "Fail 2" + !(nx != false) -> "Fail 3" + nx != x -> "Fail 4" + nx == y -> "Fail 5" + !(nx == x) -> "Fail 6" + !(nx != y) -> "Fail 7" + nn == true -> "Fail 8" + !(nn != true) -> "Fail 9" + nn == x -> "Fail 10" + !(nn != x) -> "Fail 11" + ax != true -> "Fail 12" + ax == false -> "Fail 13" + !(ax == true) -> "Fail 14" + !(ax != false) -> "Fail 15" + ax != bx -> "Fail 16" + ax == by -> "Fail 17" + !(ax == bx) -> "Fail 18" + !(ax != by) -> "Fail 19" + an == true -> "Fail 20" + !(an != true) -> "Fail 21" + an == bx -> "Fail 22" + !(an != bx) -> "Fail 23" + else -> "OK" + } +} diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveByte.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveByte.kt new file mode 100644 index 00000000000..c099545c004 --- /dev/null +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveByte.kt @@ -0,0 +1,41 @@ +// Auto-generated by GeneratePrimitiveEqualityWithNullableTestData. Do not edit! + +val nx: Byte? = 0.toByte() +val nn: Byte? = null +val x: Byte = 0.toByte() +val y: Byte = 1.toByte() + +fun box(): String { + val ax: Byte? = 0.toByte() + val an: Byte? = null + val bx: Byte = 0.toByte() + val by: Byte = 1.toByte() + + return when { + nx != 0.toByte() -> "Fail 0" + nx == 1.toByte() -> "Fail 1" + !(nx == 0.toByte()) -> "Fail 2" + !(nx != 1.toByte()) -> "Fail 3" + nx != x -> "Fail 4" + nx == y -> "Fail 5" + !(nx == x) -> "Fail 6" + !(nx != y) -> "Fail 7" + nn == 0.toByte() -> "Fail 8" + !(nn != 0.toByte()) -> "Fail 9" + nn == x -> "Fail 10" + !(nn != x) -> "Fail 11" + ax != 0.toByte() -> "Fail 12" + ax == 1.toByte() -> "Fail 13" + !(ax == 0.toByte()) -> "Fail 14" + !(ax != 1.toByte()) -> "Fail 15" + ax != bx -> "Fail 16" + ax == by -> "Fail 17" + !(ax == bx) -> "Fail 18" + !(ax != by) -> "Fail 19" + an == 0.toByte() -> "Fail 20" + !(an != 0.toByte()) -> "Fail 21" + an == bx -> "Fail 22" + !(an != bx) -> "Fail 23" + else -> "OK" + } +} diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveChar.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveChar.kt new file mode 100644 index 00000000000..7c2486b1fc6 --- /dev/null +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveChar.kt @@ -0,0 +1,41 @@ +// Auto-generated by GeneratePrimitiveEqualityWithNullableTestData. Do not edit! + +val nx: Char? = '0' +val nn: Char? = null +val x: Char = '0' +val y: Char = '1' + +fun box(): String { + val ax: Char? = '0' + val an: Char? = null + val bx: Char = '0' + val by: Char = '1' + + return when { + nx != '0' -> "Fail 0" + nx == '1' -> "Fail 1" + !(nx == '0') -> "Fail 2" + !(nx != '1') -> "Fail 3" + nx != x -> "Fail 4" + nx == y -> "Fail 5" + !(nx == x) -> "Fail 6" + !(nx != y) -> "Fail 7" + nn == '0' -> "Fail 8" + !(nn != '0') -> "Fail 9" + nn == x -> "Fail 10" + !(nn != x) -> "Fail 11" + ax != '0' -> "Fail 12" + ax == '1' -> "Fail 13" + !(ax == '0') -> "Fail 14" + !(ax != '1') -> "Fail 15" + ax != bx -> "Fail 16" + ax == by -> "Fail 17" + !(ax == bx) -> "Fail 18" + !(ax != by) -> "Fail 19" + an == '0' -> "Fail 20" + !(an != '0') -> "Fail 21" + an == bx -> "Fail 22" + !(an != bx) -> "Fail 23" + else -> "OK" + } +} diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveInt.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveInt.kt new file mode 100644 index 00000000000..4b1bb88854a --- /dev/null +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveInt.kt @@ -0,0 +1,41 @@ +// Auto-generated by GeneratePrimitiveEqualityWithNullableTestData. Do not edit! + +val nx: Int? = 0 +val nn: Int? = null +val x: Int = 0 +val y: Int = 1 + +fun box(): String { + val ax: Int? = 0 + val an: Int? = null + val bx: Int = 0 + val by: Int = 1 + + return when { + nx != 0 -> "Fail 0" + nx == 1 -> "Fail 1" + !(nx == 0) -> "Fail 2" + !(nx != 1) -> "Fail 3" + nx != x -> "Fail 4" + nx == y -> "Fail 5" + !(nx == x) -> "Fail 6" + !(nx != y) -> "Fail 7" + nn == 0 -> "Fail 8" + !(nn != 0) -> "Fail 9" + nn == x -> "Fail 10" + !(nn != x) -> "Fail 11" + ax != 0 -> "Fail 12" + ax == 1 -> "Fail 13" + !(ax == 0) -> "Fail 14" + !(ax != 1) -> "Fail 15" + ax != bx -> "Fail 16" + ax == by -> "Fail 17" + !(ax == bx) -> "Fail 18" + !(ax != by) -> "Fail 19" + an == 0 -> "Fail 20" + !(an != 0) -> "Fail 21" + an == bx -> "Fail 22" + !(an != bx) -> "Fail 23" + else -> "OK" + } +} diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveLong.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveLong.kt new file mode 100644 index 00000000000..168709326f1 --- /dev/null +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveLong.kt @@ -0,0 +1,41 @@ +// Auto-generated by GeneratePrimitiveEqualityWithNullableTestData. Do not edit! + +val nx: Long? = 0L +val nn: Long? = null +val x: Long = 0L +val y: Long = 1L + +fun box(): String { + val ax: Long? = 0L + val an: Long? = null + val bx: Long = 0L + val by: Long = 1L + + return when { + nx != 0L -> "Fail 0" + nx == 1L -> "Fail 1" + !(nx == 0L) -> "Fail 2" + !(nx != 1L) -> "Fail 3" + nx != x -> "Fail 4" + nx == y -> "Fail 5" + !(nx == x) -> "Fail 6" + !(nx != y) -> "Fail 7" + nn == 0L -> "Fail 8" + !(nn != 0L) -> "Fail 9" + nn == x -> "Fail 10" + !(nn != x) -> "Fail 11" + ax != 0L -> "Fail 12" + ax == 1L -> "Fail 13" + !(ax == 0L) -> "Fail 14" + !(ax != 1L) -> "Fail 15" + ax != bx -> "Fail 16" + ax == by -> "Fail 17" + !(ax == bx) -> "Fail 18" + !(ax != by) -> "Fail 19" + an == 0L -> "Fail 20" + !(an != 0L) -> "Fail 21" + an == bx -> "Fail 22" + !(an != bx) -> "Fail 23" + else -> "OK" + } +} diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveShort.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveShort.kt new file mode 100644 index 00000000000..9c8edf36e32 --- /dev/null +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveShort.kt @@ -0,0 +1,41 @@ +// Auto-generated by GeneratePrimitiveEqualityWithNullableTestData. Do not edit! + +val nx: Short? = 0.toShort() +val nn: Short? = null +val x: Short = 0.toShort() +val y: Short = 1.toShort() + +fun box(): String { + val ax: Short? = 0.toShort() + val an: Short? = null + val bx: Short = 0.toShort() + val by: Short = 1.toShort() + + return when { + nx != 0.toShort() -> "Fail 0" + nx == 1.toShort() -> "Fail 1" + !(nx == 0.toShort()) -> "Fail 2" + !(nx != 1.toShort()) -> "Fail 3" + nx != x -> "Fail 4" + nx == y -> "Fail 5" + !(nx == x) -> "Fail 6" + !(nx != y) -> "Fail 7" + nn == 0.toShort() -> "Fail 8" + !(nn != 0.toShort()) -> "Fail 9" + nn == x -> "Fail 10" + !(nn != x) -> "Fail 11" + ax != 0.toShort() -> "Fail 12" + ax == 1.toShort() -> "Fail 13" + !(ax == 0.toShort()) -> "Fail 14" + !(ax != 1.toShort()) -> "Fail 15" + ax != bx -> "Fail 16" + ax == by -> "Fail 17" + !(ax == bx) -> "Fail 18" + !(ax != by) -> "Fail 19" + an == 0.toShort() -> "Fail 20" + !(an != 0.toShort()) -> "Fail 21" + an == bx -> "Fail 22" + !(an != bx) -> "Fail 23" + else -> "OK" + } +} diff --git a/compiler/testData/codegen/bytecodeText/noBoxingForBoxedEqPrimitive.kt b/compiler/testData/codegen/bytecodeText/noBoxingForBoxedEqPrimitive.kt new file mode 100644 index 00000000000..1ab23a094e2 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/noBoxingForBoxedEqPrimitive.kt @@ -0,0 +1,26 @@ +fun testBoolean1(a: Boolean?, b: Boolean) = a == b +fun testBoolean2(a: Boolean?, b: Boolean) = a != b + +fun testChar1(a: Char?, b: Char) = a == b +fun testChar2(a: Char?, b: Char) = a != b + +fun testByte1(a: Byte?, b: Byte) = a == b +fun testByte2(a: Byte?, b: Byte) = a != b + +fun testShort1(a: Short?, b: Short) = a == b +fun testShort2(a: Short?, b: Short) = a != b + +fun testInt1(a: Int?, b: Int) = a == b +fun testInt2(a: Int?, b: Int) = a != b + +fun testLong1(a: Long?, b: Long) = a == b +fun testLong2(a: Long?, b: Long) = a != b + +// 2 booleanValue +// 2 charValue +// 2 byteValue +// 2 shortValue +// 2 intValue +// 2 longValue +// 0 valueOf +// 0 areEqual \ No newline at end of file diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 9d5279219a3..af4075c5bbe 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -12499,6 +12499,66 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/unboxComparable.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class EqualityWithNullable extends AbstractIrBlackBoxCodegenTest { + public void testAllFilesPresentInEqualityWithNullable() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("boxedEqPrimitiveEvaluationOrder.kt") + public void testBoxedEqPrimitiveEvaluationOrder() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/boxedEqPrimitiveEvaluationOrder.kt"); + doTest(fileName); + } + + @TestMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Generated extends AbstractIrBlackBoxCodegenTest { + public void testAllFilesPresentInGenerated() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("boxedEqPrimitiveBoolean.kt") + public void testBoxedEqPrimitiveBoolean() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveBoolean.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveByte.kt") + public void testBoxedEqPrimitiveByte() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveByte.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveChar.kt") + public void testBoxedEqPrimitiveChar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveChar.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveInt.kt") + public void testBoxedEqPrimitiveInt() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveInt.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveLong.kt") + public void testBoxedEqPrimitiveLong() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveLong.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveShort.kt") + public void testBoxedEqPrimitiveShort() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveShort.kt"); + doTest(fileName); + } + } + } } @TestMetadata("compiler/testData/codegen/box/private") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 2f510dcf7cc..d6d04a31ab5 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -12499,6 +12499,66 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/unboxComparable.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class EqualityWithNullable extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInEqualityWithNullable() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("boxedEqPrimitiveEvaluationOrder.kt") + public void testBoxedEqPrimitiveEvaluationOrder() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/boxedEqPrimitiveEvaluationOrder.kt"); + doTest(fileName); + } + + @TestMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Generated extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInGenerated() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("boxedEqPrimitiveBoolean.kt") + public void testBoxedEqPrimitiveBoolean() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveBoolean.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveByte.kt") + public void testBoxedEqPrimitiveByte() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveByte.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveChar.kt") + public void testBoxedEqPrimitiveChar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveChar.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveInt.kt") + public void testBoxedEqPrimitiveInt() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveInt.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveLong.kt") + public void testBoxedEqPrimitiveLong() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveLong.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveShort.kt") + public void testBoxedEqPrimitiveShort() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveShort.kt"); + doTest(fileName); + } + } + } } @TestMetadata("compiler/testData/codegen/box/private") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index e0d7161dd8f..671b77dd54b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -294,6 +294,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { doTest(fileName); } + @TestMetadata("noBoxingForBoxedEqPrimitive.kt") + public void testNoBoxingForBoxedEqPrimitive() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/noBoxingForBoxedEqPrimitive.kt"); + doTest(fileName); + } + @TestMetadata("noFlagAnnotations.kt") public void testNoFlagAnnotations() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/noFlagAnnotations.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 057d48c4997..90eb0eed634 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -12499,6 +12499,66 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/unboxComparable.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class EqualityWithNullable extends AbstractLightAnalysisModeTest { + public void testAllFilesPresentInEqualityWithNullable() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("boxedEqPrimitiveEvaluationOrder.kt") + public void testBoxedEqPrimitiveEvaluationOrder() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/boxedEqPrimitiveEvaluationOrder.kt"); + doTest(fileName); + } + + @TestMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Generated extends AbstractLightAnalysisModeTest { + public void testAllFilesPresentInGenerated() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("boxedEqPrimitiveBoolean.kt") + public void testBoxedEqPrimitiveBoolean() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveBoolean.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveByte.kt") + public void testBoxedEqPrimitiveByte() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveByte.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveChar.kt") + public void testBoxedEqPrimitiveChar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveChar.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveInt.kt") + public void testBoxedEqPrimitiveInt() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveInt.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveLong.kt") + public void testBoxedEqPrimitiveLong() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveLong.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveShort.kt") + public void testBoxedEqPrimitiveShort() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveShort.kt"); + doTest(fileName); + } + } + } } @TestMetadata("compiler/testData/codegen/box/private") diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GeneratePrimitiveEqualityWithNullableTestData.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GeneratePrimitiveEqualityWithNullableTestData.kt new file mode 100644 index 00000000000..34e2b9fa8cf --- /dev/null +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GeneratePrimitiveEqualityWithNullableTestData.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.generators.tests + +import com.intellij.openapi.util.io.FileUtil +import java.io.File +import java.io.PrintWriter + +object GeneratePrimitiveEqualityWithNullableTestData { + private val TEST_DATA_DIR = File("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable") + private val GENERATED_DIR = File(TEST_DATA_DIR, "generated") + + private val PREAMBLE_MESSAGE = "Auto-generated by ${this::class.java.simpleName}. Do not edit!" + + private fun generateBoxedVsPrimitiveTest(type: String, x: String, y: String) { + PrintWriter(File(GENERATED_DIR, "boxedEqPrimitive$type.kt")).use { + it.generateBoxedVsPrimitiveTestBody(type, x, y) + } + } + + private fun PrintWriter.generateBoxedVsPrimitiveTestBody(type: String, x: String, y: String) { + println("// $PREAMBLE_MESSAGE") + println() + println("val nx: $type? = $x") + println("val nn: $type? = null") + println("val x: $type = $x") + println("val y: $type = $y") + println() + println("fun box(): String {") + println(" val ax: $type? = $x") + println(" val an: $type? = null") + println(" val bx: $type = $x") + println(" val by: $type = $y") + println() + + println(" return when {") + + listOf( + "nx != $x", + "nx == $y", + "!(nx == $x)", + "!(nx != $y)", + "nx != x", + "nx == y", + "!(nx == x)", + "!(nx != y)", + "nn == $x", + "!(nn != $x)", + "nn == x", + "!(nn != x)", + "ax != $x", + "ax == $y", + "!(ax == $x)", + "!(ax != $y)", + "ax != bx", + "ax == by", + "!(ax == bx)", + "!(ax != by)", + "an == $x", + "!(an != $x)", + "an == bx", + "!(an != bx)" + ).forEachIndexed { i, condition -> + println(" $condition -> \"Fail $i\"") + } + + println(" else -> \"OK\"") + println(" }") + println("}") + } + + @JvmStatic + fun main(args: Array) { + if (!TEST_DATA_DIR.exists()) throw AssertionError("${TEST_DATA_DIR.path} doesn't exist") + + FileUtil.delete(GENERATED_DIR) + GENERATED_DIR.mkdirs() + + generateBoxedVsPrimitiveTest("Boolean", "true", "false") + generateBoxedVsPrimitiveTest("Char", "'0'", "'1'") + generateBoxedVsPrimitiveTest("Byte", "0.toByte()", "1.toByte()") + generateBoxedVsPrimitiveTest("Short", "0.toShort()", "1.toShort()") + generateBoxedVsPrimitiveTest("Int", "0", "1") + generateBoxedVsPrimitiveTest("Long", "0L", "1L") + } +} \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 33f6b885210..8a208bb2bb8 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -13957,6 +13957,66 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/unboxComparable.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class EqualityWithNullable extends AbstractJsCodegenBoxTest { + public void testAllFilesPresentInEqualityWithNullable() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("boxedEqPrimitiveEvaluationOrder.kt") + public void testBoxedEqPrimitiveEvaluationOrder() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/boxedEqPrimitiveEvaluationOrder.kt"); + doTest(fileName); + } + + @TestMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Generated extends AbstractJsCodegenBoxTest { + public void testAllFilesPresentInGenerated() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("boxedEqPrimitiveBoolean.kt") + public void testBoxedEqPrimitiveBoolean() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveBoolean.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveByte.kt") + public void testBoxedEqPrimitiveByte() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveByte.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveChar.kt") + public void testBoxedEqPrimitiveChar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveChar.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveInt.kt") + public void testBoxedEqPrimitiveInt() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveInt.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveLong.kt") + public void testBoxedEqPrimitiveLong() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveLong.kt"); + doTest(fileName); + } + + @TestMetadata("boxedEqPrimitiveShort.kt") + public void testBoxedEqPrimitiveShort() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithNullable/generated/boxedEqPrimitiveShort.kt"); + doTest(fileName); + } + } + } } @TestMetadata("compiler/testData/codegen/box/private")