| // TARGET_BACKEND: JS_IR, JS_IR_ES6 | |
| // The test checks an optimization which is implemented only for JS_IR backend | |
| @file:Suppress("RESERVED_MEMBER_INSIDE_VALUE_CLASS") | |
| // FILE: lib.kt | |
| inline class ClassInt(val x: Int) | |
| inline class ClassString(val x: String) | |
| inline class ClassUnderlayingInline(val x: ClassInt) | |
| inline class ClassNullableInt(val x: Int?) | |
| inline class ClassNullableUnderlayingInline(val x: ClassInt?) | |
| inline class ClassNothing(val x: Nothing?) | |
| value class ClassWithEqualsOverride(val data: Int) { | |
| override fun equals(other: Any?): Boolean = other is ClassWithEqualsOverride && data % 256 == other.data % 256 | |
| } | |
| class MyClass(val data: Int) { | |
| override fun equals(other: Any?): Boolean = other is MyClass && data % 10 == other.data % 10 | |
| } | |
| inline class ClassUnderlayingWithEquals(val x: MyClass) | |
| interface InterfaceForInlineClass | |
| inline class ClassIntWithInterface(val x: Int) : InterfaceForInlineClass | |
| inline class ClassStringWithInterface(val x: String) : InterfaceForInlineClass | |
| // FILE: main.kt | |
| // CHECK_NOT_CALLED_IN_SCOPE: scope=testBasicInt function=equals | |
| // CHECK_NEW_COUNT: function=testBasicInt count=0 | |
| fun testBasicInt() { | |
| val a1_1 = ClassInt(1) | |
| val a1_2 = ClassInt(1) | |
| val a2 = ClassInt(2) | |
| assertTrue(a1_1 == a1_1) | |
| assertTrue(a1_1 == a1_2) | |
| assertFalse(a1_1 == a2) | |
| } | |
| // CHECK_NOT_CALLED_IN_SCOPE: scope=testBasicString function=equals | |
| // CHECK_NEW_COUNT: function=testBasicString count=0 | |
| fun testBasicString() { | |
| val b1_1 = ClassString("b1") | |
| val b1_2 = ClassString("b1") | |
| val b2 = ClassString("b2") | |
| assertTrue(b1_1 == b1_1) | |
| assertTrue(b1_1 == b1_2) | |
| assertFalse(b1_1 == b2) | |
| } | |
| // CHECK_NOT_CALLED_IN_SCOPE: scope=testFunctionCall function=equals | |
| // CHECK_NEW_COUNT: function=testFunctionCall count=0 | |
| fun testFunctionCall() { | |
| // CHECK_NEW_COUNT: function=testFunctionCall$makeClassInt count=0 | |
| fun makeClassInt(x: Int): ClassInt { | |
| return ClassInt(x) | |
| } | |
| val a1 = ClassInt(1) | |
| assertTrue(a1 == makeClassInt(1)) | |
| assertTrue(makeClassInt(1) == a1) | |
| assertTrue(makeClassInt(1) == makeClassInt(1)) | |
| assertFalse(a1 == makeClassInt(2)) | |
| assertFalse(makeClassInt(2) == a1) | |
| assertFalse(makeClassInt(2) == makeClassInt(3)) | |
| } | |
| // CHECK_NEW_COUNT: function=testTypeErasing count=3 | |
| fun testTypeErasing() { | |
| val a1 = ClassInt(1) | |
| // CHECK_CALLED_IN_SCOPE: scope=testTypeErasing$isEqualsWithA1 function=equals | |
| // CHECK_NEW_COUNT: function=testTypeErasing$isEqualsWithA1 count=1 | |
| fun <T> isEqualsWithA1(a: T): Boolean { | |
| return a1 == a | |
| } | |
| assertTrue(isEqualsWithA1(a1)) | |
| assertTrue(isEqualsWithA1(ClassInt(1))) | |
| val a2 = ClassInt(2) | |
| assertFalse(isEqualsWithA1(a2)) | |
| } | |
| // CHECK_NOT_CALLED_IN_SCOPE: scope=testFunctionCall function=equals | |
| // CHECK_NEW_COUNT: function=testTypeErasingAndCast count=4 | |
| fun testTypeErasingAndCast() { | |
| fun boxValue(a: Any): ClassInt { | |
| return a as ClassInt | |
| } | |
| val a1_1 = ClassInt(1) | |
| val a1_2 = ClassInt(1) | |
| assertTrue(boxValue(a1_1) == a1_2) | |
| assertTrue(boxValue(a1_1) == boxValue(a1_2)) | |
| assertTrue(ClassInt(if (a1_1 == boxValue(a1_2)) 1 else 0) == a1_1) | |
| } | |
| // CHECK_CALLED_IN_SCOPE: scope=testNullableInstances function=equals | |
| // CHECK_NEW_COUNT: function=testNullableInstances count=21 | |
| fun testNullableInstances() { | |
| val a1_1: ClassInt? = ClassInt(1) | |
| val a1_2 = ClassInt(1) | |
| val a1_3: ClassInt? = null | |
| val a1_4: ClassInt? = ClassInt(1) | |
| assertTrue(a1_1 == a1_2) | |
| assertTrue(a1_2 == a1_1) | |
| assertTrue(a1_1!! == a1_2) | |
| assertTrue(a1_2 == a1_1!!) | |
| assertTrue(a1_4 == a1_1!!) | |
| assertTrue(a1_4!! == a1_1!!) | |
| val a1_5 = a1_4!! | |
| assertTrue(a1_5 == a1_1!!) | |
| assertFalse(a1_1 == a1_3) | |
| assertFalse(a1_2 == a1_3) | |
| assertFalse(a1_3 == a1_2) | |
| } | |
| fun testUnderlyingInline() { | |
| val x1: ClassInt? = ClassInt(1) | |
| val c1_1 = ClassUnderlayingInline(ClassInt(1)) | |
| val c1_2 = ClassUnderlayingInline(ClassInt(1)) | |
| val c2 = ClassUnderlayingInline(ClassInt(2)) | |
| // CHECK_NOT_CALLED_IN_SCOPE: scope=testUnderlyingInline$caseJsEq function=equals | |
| // CHECK_NEW_COUNT: function=testUnderlyingInline$caseJsEq count=0 | |
| fun caseJsEq() { | |
| assertTrue(c1_1 == c1_2) | |
| assertFalse(c1_1 == c2) | |
| val a1 = ClassInt(1) | |
| assertTrue(c1_1.x == a1) | |
| assertTrue(c1_1.x == c1_2.x) | |
| assertFalse(a1 == c2.x) | |
| } | |
| caseJsEq() | |
| // CHECK_NOT_CALLED_IN_SCOPE: scope=testUnderlyingInline$caseEquals function=equals | |
| // CHECK_NEW_COUNT: function=testUnderlyingInline$caseEquals count=2 | |
| fun caseEquals() { | |
| assertTrue(c1_1 == ClassUnderlayingInline(x1!!)) | |
| assertTrue(ClassUnderlayingInline(x1!!) == c1_2) | |
| } | |
| caseEquals() | |
| } | |
| // CHECK_CALLED_IN_SCOPE: scope=testEqualsOverride function=equals | |
| // CHECK_NEW_COUNT: function=testEqualsOverride count=14 | |
| fun testEqualsOverride() { | |
| val d1 = ClassWithEqualsOverride(1) | |
| val d2 = ClassWithEqualsOverride(2) | |
| val d257 = ClassWithEqualsOverride(257) | |
| val d1_1: ClassWithEqualsOverride? = ClassWithEqualsOverride(513) | |
| val d1_2: ClassWithEqualsOverride? = null | |
| assertTrue(d1 == d257) | |
| assertTrue(d1_1 == d1) | |
| assertTrue(d1 == d1_1) | |
| assertFalse(d1 == d2) | |
| assertFalse(d1_2 == d1) | |
| assertFalse(d1 == d1_2) | |
| assertFalse(d1_1 == d1_2) | |
| } | |
| // CHECK_NEW_COUNT: function=testNullableUnderlyingType count=1 | |
| fun testNullableUnderlyingType() { | |
| val x0 = ClassNullableInt(0) | |
| val x1_1 = ClassNullableInt(1) | |
| val x1_2 = ClassNullableInt(1) | |
| val x_null_1 = ClassNullableInt(null) | |
| val x_null_2 = ClassNullableInt(null) | |
| val nullable_1: ClassNullableInt? = null | |
| val nullable_2: ClassNullableInt? = ClassNullableInt(null) | |
| // CHECK_NOT_CALLED_IN_SCOPE: scope=testNullableUnderlyingType$caseEquals function=equals | |
| // CHECK_NEW_COUNT: function=testNullableUnderlyingType$caseEquals count=0 | |
| fun caseEquals() { | |
| assertTrue(x0 == x0) | |
| assertTrue(x0 == ClassNullableInt(0)) | |
| assertTrue(x1_1 == x1_2) | |
| assertTrue(x_null_1 == x_null_2) | |
| assertTrue(x_null_1 == ClassNullableInt(null)) | |
| assertTrue(x_null_1.x == null) | |
| assertTrue(nullable_1 == null) | |
| assertTrue(nullable_1?.x == null) | |
| assertTrue(nullable_2!!.x == null) | |
| assertTrue(nullable_1?.x == nullable_2!!.x) | |
| assertFalse(x0 == x_null_2) | |
| assertFalse(x1_1 == x0) | |
| assertFalse(x_null_1 == x1_2) | |
| assertFalse(ClassNullableInt(null) == ClassNullableInt(0)) | |
| assertFalse(nullable_2 == null) | |
| } | |
| caseEquals() | |
| // CHECK_CALLED_IN_SCOPE: scope=testNullableUnderlyingType$caseJsEq function=equals | |
| // CHECK_NEW_COUNT: function=testNullableUnderlyingType$caseJsEq count=0 | |
| fun caseJsEq() { | |
| assertFalse(nullable_1 == nullable_2) | |
| } | |
| caseJsEq() | |
| } | |
| // CHECK_NEW_COUNT: function=testUnderlyingWithEqualsOverride count=4 TARGET_BACKENDS=JS_IR | |
| // CHECK_CALLED_IN_SCOPE: scope=testUnderlyingWithEqualsOverride function=equals | |
| fun testUnderlyingWithEqualsOverride() { | |
| val x0 = ClassUnderlayingWithEquals(MyClass(0)) | |
| val x10 = ClassUnderlayingWithEquals(MyClass(10)) | |
| val x1_1 = ClassUnderlayingWithEquals(MyClass(1)) | |
| val x1_2 = ClassUnderlayingWithEquals(MyClass(1)) | |
| assertTrue(x0 == x0) | |
| assertTrue(x0 == x10) | |
| assertTrue(x1_1 == x1_2) | |
| assertFalse(x1_1 == x0) | |
| assertFalse(x10 == x1_2) | |
| } | |
| // CHECK_NEW_COUNT: function=testNullableUnderlyingInlineClass count=1 | |
| fun testNullableUnderlyingInlineClass() { | |
| val i1_1: ClassInt? = ClassInt(1) | |
| val i1_2: ClassInt? = ClassInt(1) | |
| val x_null = ClassNullableUnderlayingInline(null) | |
| val x0 = ClassNullableUnderlayingInline(ClassInt(0)) | |
| val x1_1 = ClassNullableUnderlayingInline(ClassInt(1)) | |
| val x1_2 = ClassNullableUnderlayingInline(ClassInt(1)) | |
| val x1_3 = ClassNullableUnderlayingInline(i1_1) | |
| val x1_4 = ClassNullableUnderlayingInline(i1_2!!) | |
| // CHECK_NOT_CALLED_IN_SCOPE: scope=testNullableUnderlyingInlineClass$caseJsEq function=equals | |
| // CHECK_NEW_COUNT: function=testNullableUnderlyingInlineClass$caseJsEq count=0 | |
| fun caseJsEq() { | |
| assertTrue(x_null == x_null) | |
| assertTrue(x1_1 == x1_2) | |
| assertTrue(x0 == ClassNullableUnderlayingInline(ClassInt(0))) | |
| assertTrue(x1_1 == x1_3) | |
| assertTrue(x1_4 == x1_3) | |
| assertTrue(x1_4 == x1_2) | |
| assertTrue(x_null == ClassNullableUnderlayingInline(null)) | |
| assertTrue(ClassNullableUnderlayingInline(null) == x_null) | |
| assertFalse(x_null == x0) | |
| assertFalse(x0 == x_null) | |
| assertFalse(x1_1 == x_null) | |
| assertFalse(ClassNullableUnderlayingInline(null) == x1_4) | |
| } | |
| caseJsEq() | |
| // CHECK_NOT_CALLED_IN_SCOPE: scope=testNullableUnderlyingInlineClass$caseEquals function=equals | |
| // CHECK_NEW_COUNT: function=testNullableUnderlyingInlineClass$caseEquals count=1 | |
| fun caseEquals() { | |
| assertTrue(ClassNullableUnderlayingInline(i1_1) == ClassNullableUnderlayingInline(i1_2!!)) | |
| } | |
| caseEquals() | |
| } | |
| // CHECK_CALLED_IN_SCOPE: scope=testInlineClassWithInterface function=equals | |
| // CHECK_NEW_COUNT: function=testInlineClassWithInterface count=10 | |
| fun testInlineClassWithInterface() { | |
| val xi_1_1: InterfaceForInlineClass = ClassIntWithInterface(1) | |
| val xi_1_2: InterfaceForInlineClass = ClassIntWithInterface(1) | |
| val x_1 = ClassIntWithInterface(1) | |
| val x_2 = ClassIntWithInterface(2) | |
| val yi_foo: InterfaceForInlineClass = ClassStringWithInterface("foo") | |
| val y_foo = ClassStringWithInterface("foo") | |
| val y_bar = ClassStringWithInterface("bar") | |
| assertTrue(xi_1_1 == x_1) | |
| assertTrue(xi_1_1 == xi_1_2) | |
| assertTrue(yi_foo == y_foo) | |
| assertTrue(y_foo == yi_foo) | |
| assertTrue(xi_1_1 as ClassIntWithInterface == x_1) | |
| assertTrue(x_1 == xi_1_1 as ClassIntWithInterface) | |
| assertTrue(xi_1_2 as ClassIntWithInterface == xi_1_1 as ClassIntWithInterface) | |
| assertTrue(xi_1_2 as? ClassIntWithInterface == xi_1_1 as? ClassIntWithInterface) | |
| assertFalse(xi_1_1 == x_2) | |
| assertFalse(yi_foo == y_bar) | |
| assertFalse(xi_1_2 == yi_foo) | |
| } | |
| // CHECK_CALLED_IN_SCOPE: scope=testCompareDifferentInstancesInSmartCast function=equals | |
| // CHECK_NEW_COUNT: function=testCompareDifferentInstancesInSmartCast count=6 | |
| @Suppress("EQUALITY_NOT_APPLICABLE") | |
| fun testCompareDifferentInstancesInSmartCast() { | |
| val x1_1: Any = ClassInt(1) | |
| val x1_2 = ClassInt(1) | |
| val y1: Any = ClassString("1") | |
| val y_foo: Any = ClassString("foo") | |
| if (x1_1 is ClassInt && y1 is ClassString) { | |
| assertTrue(x1_2 == x1_1) | |
| assertFalse(x1_1 == y1) | |
| assertFalse(x1_2 == y1) | |
| } | |
| if (x1_1 is ClassInt && y_foo is ClassString) { | |
| assertFalse(x1_1 == y_foo) | |
| assertFalse(x1_2 == y_foo) | |
| assertFalse(y1 == y_foo) | |
| } | |
| } | |
| fun testCompareDifferentInstncesInInlineTemplate() { | |
| inline fun <reified T, reified S> myEq(x: T, y: S) = x == y | |
| // CHECK_NEW_COUNT: function=testCompareDifferentInstncesInInlineTemplate$caseJsEq count=0 | |
| fun caseJsEq() { | |
| assertTrue(myEq(ClassInt(1), ClassInt(1))) | |
| assertTrue(myEq(ClassString("foo"), ClassString("foo"))) | |
| assertFalse(myEq(ClassInt(1), ClassInt(2))) | |
| assertFalse(myEq(ClassString("foo"), ClassString("bar"))) | |
| } | |
| caseJsEq() | |
| // CHECK_CALLED_IN_SCOPE: scope=testCompareDifferentInstncesInInlineTemplate$caseEquals function=equals | |
| // CHECK_NEW_COUNT: function=testCompareDifferentInstncesInInlineTemplate$caseEquals count=4 | |
| fun caseEquals() { | |
| assertFalse(myEq(ClassInt(1), ClassString("bar"))) | |
| assertFalse(myEq(ClassInt(1), ClassString("1"))) | |
| } | |
| caseEquals() | |
| } | |
| // CHECK_CALLED_IN_SCOPE: scope=testNothing function=equals | |
| // CHECK_NEW_COUNT: function=testNothing count=0 | |
| fun testNothing() { | |
| val x_undefined_1 = ClassNothing(undefined) | |
| val x_undefined_2 = ClassNothing(undefined) | |
| val x_null_1 = ClassNothing(null) | |
| val x_null_2 = ClassNothing(null) | |
| assertTrue(x_undefined_1 == x_undefined_1) | |
| assertTrue(x_undefined_1 == x_undefined_2) | |
| assertTrue(x_undefined_1 == ClassNothing(undefined)) | |
| assertTrue(x_null_1 == x_null_1) | |
| assertTrue(x_null_1 == x_null_2) | |
| assertTrue(x_null_1 == ClassNothing(null)) | |
| assertTrue(x_undefined_1 == x_null_1) | |
| assertTrue(x_null_1 == x_undefined_1) | |
| assertTrue(x_undefined_1 == ClassNothing(null)) | |
| assertTrue(ClassNothing(null) == x_undefined_1) | |
| assertTrue(ClassNothing(undefined) == x_null_1) | |
| assertTrue(x_null_1 == ClassNothing(undefined)) | |
| } | |
| // CHECK_CALLED_IN_SCOPE: scope=testNullableNothing function=equals | |
| // CHECK_NEW_COUNT: function=testNullableNothing count=2 | |
| fun testNullableNothing() { | |
| val x_nothing_undefined: ClassNothing? = ClassNothing(undefined) | |
| val x_nothing_null: ClassNothing? = ClassNothing(null) | |
| val x_null: ClassNothing? = null | |
| assertTrue(x_nothing_undefined == x_nothing_null) | |
| assertTrue(x_null == null) | |
| assertTrue(x_null == undefined) | |
| assertTrue(null == x_null) | |
| assertTrue(undefined == x_null) | |
| assertFalse(undefined == x_nothing_null) | |
| assertFalse(x_nothing_null == undefined) | |
| assertFalse(undefined == x_nothing_undefined) | |
| assertFalse(x_nothing_undefined == undefined) | |
| assertFalse(x_null == x_nothing_null) | |
| assertFalse(x_null == x_nothing_undefined) | |
| assertFalse(x_nothing_null == x_null) | |
| assertFalse(x_nothing_undefined == x_null) | |
| } | |
| fun box(): String { | |
| testBasicInt() | |
| testBasicString() | |
| testFunctionCall() | |
| testTypeErasing() | |
| testTypeErasingAndCast() | |
| testNullableInstances() | |
| testUnderlyingInline() | |
| testEqualsOverride() | |
| testNullableUnderlyingType() | |
| testUnderlyingWithEqualsOverride() | |
| testNullableUnderlyingInlineClass() | |
| testInlineClassWithInterface() | |
| testCompareDifferentInstancesInSmartCast() | |
| testCompareDifferentInstncesInInlineTemplate() | |
| testNothing() | |
| testNullableNothing() | |
| return "OK" | |
| } |