Leave boxing for compareTo/areEqual methods for inline classes

Inline classes can override methods and thus introduce side effects
This commit is contained in:
Mikhail Zarechenskiy
2018-07-20 03:05:11 +03:00
parent 4e7718bfff
commit ec9d8e580e
10 changed files with 58 additions and 7 deletions
@@ -15,7 +15,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.builtins.PrimitiveType;
import org.jetbrains.kotlin.builtins.UnsignedTypes;
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
import org.jetbrains.kotlin.codegen.context.CodegenContext;
import org.jetbrains.kotlin.codegen.intrinsics.HashCode;
@@ -148,6 +147,10 @@ public class AsmUtil {
return primitiveTypeByBoxedType.get(boxedType);
}
public static boolean isBoxedPrimitiveType(@NotNull Type boxedType) {
return primitiveTypeByBoxedType.get(boxedType) != null;
}
@NotNull
public static Type unboxUnlessPrimitive(@NotNull Type boxedOrPrimitiveType) {
if (isPrimitive(boxedOrPrimitiveType)) return boxedOrPrimitiveType;
@@ -67,6 +67,7 @@ class BoxedValueDescriptor(
var isSafeToRemove = true; private set
val unboxedType: Type = getUnboxedType(boxedType, generationState)
val isInlineClassValue = isInlineClassValue(boxedType)
fun getAssociatedInsns() = associatedInsns.toList()
@@ -120,3 +121,7 @@ fun unboxedTypeOfInlineClass(boxedType: Type, state: GenerationState): Type? {
val descriptor = state.jvmBackendClassResolver.resolveToClassDescriptors(boxedType).singleOrNull() ?: return null
return state.typeMapper.mapType(descriptor.defaultType)
}
private fun isInlineClassValue(boxedType: Type): Boolean {
return !AsmUtil.isBoxedPrimitiveType(boxedType) && boxedType != AsmTypes.K_CLASS_TYPE
}
@@ -276,14 +276,15 @@ fun isProgressionClass(type: Type) =
isRangeOrProgression(buildFqNameByInternal(type.internalName))
fun AbstractInsnNode.isAreEqualIntrinsicForSameTypedBoxedValues(values: List<BasicValue>) =
isAreEqualIntrinsic() && areSameTypedBoxedValues(values)
isAreEqualIntrinsic() && areSameTypedPrimitiveBoxedValues(values)
fun areSameTypedBoxedValues(values: List<BasicValue>): Boolean {
fun areSameTypedPrimitiveBoxedValues(values: List<BasicValue>): Boolean {
if (values.size != 2) return false
val (v1, v2) = values
return v1 is BoxedBasicValue &&
v2 is BoxedBasicValue &&
v1.descriptor.unboxedType == v2.descriptor.unboxedType
v1.descriptor.unboxedType == v2.descriptor.unboxedType &&
!v1.descriptor.isInlineClassValue && !v2.descriptor.isInlineClassValue
}
fun AbstractInsnNode.isAreEqualIntrinsic() =
@@ -299,7 +300,7 @@ fun canValuesBeUnboxedForAreEqual(values: List<BasicValue>, generationState: Gen
values.none { getUnboxedType(it.type, generationState) in shouldUseEqualsForWrappers }
fun AbstractInsnNode.isJavaLangComparableCompareToForSameTypedBoxedValues(values: List<BasicValue>) =
isJavaLangComparableCompareTo() && areSameTypedBoxedValues(values)
isJavaLangComparableCompareTo() && areSameTypedPrimitiveBoxedValues(values)
fun AbstractInsnNode.isJavaLangComparableCompareTo() =
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
@@ -0,0 +1,17 @@
// !LANGUAGE: +InlineClasses
// IGNORE_BACKEND: JVM_IR
inline class Foo(val x: Int) : Comparable<Foo> {
override fun compareTo(other: Foo): Int {
return 10
}
}
fun box(): String {
val f1 = Foo(42)
val ff1: Comparable<Foo> = f1
if (ff1.compareTo(f1) != 10) return "Fail"
return "OK"
}
@@ -20,8 +20,8 @@ fun getAndCheckInlinedInt(a: InlinedInt, b: InlinedInt) =
// @TestInlinedKt.class:
// 0 valueOf
// 0 Value
// 0 areEqual
// 0 INVOKESTATIC InlinedInt\$Erased.box
// 1 areEqual
// 2 INVOKESTATIC InlinedInt\$Erased.box
// 0 INVOKEVIRTUAL InlinedInt.unbox
// FILE: Inline.kt
@@ -11196,6 +11196,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/inlineClasses/computablePropertyInsideInlineClass.kt");
}
@TestMetadata("conformToComparableAndCallInterfaceMethod.kt")
public void testConformToComparableAndCallInterfaceMethod() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/conformToComparableAndCallInterfaceMethod.kt");
}
@TestMetadata("correctBoxingForBranchExpressions.kt")
public void testCorrectBoxingForBranchExpressions() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/correctBoxingForBranchExpressions.kt");
@@ -11196,6 +11196,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/inlineClasses/computablePropertyInsideInlineClass.kt");
}
@TestMetadata("conformToComparableAndCallInterfaceMethod.kt")
public void testConformToComparableAndCallInterfaceMethod() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/conformToComparableAndCallInterfaceMethod.kt");
}
@TestMetadata("correctBoxingForBranchExpressions.kt")
public void testCorrectBoxingForBranchExpressions() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/correctBoxingForBranchExpressions.kt");
@@ -11196,6 +11196,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/inlineClasses/computablePropertyInsideInlineClass.kt");
}
@TestMetadata("conformToComparableAndCallInterfaceMethod.kt")
public void testConformToComparableAndCallInterfaceMethod() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/conformToComparableAndCallInterfaceMethod.kt");
}
@TestMetadata("correctBoxingForBranchExpressions.kt")
public void testCorrectBoxingForBranchExpressions() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/correctBoxingForBranchExpressions.kt");
@@ -9826,6 +9826,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/inlineClasses/computablePropertyInsideInlineClass.kt");
}
@TestMetadata("conformToComparableAndCallInterfaceMethod.kt")
public void testConformToComparableAndCallInterfaceMethod() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/conformToComparableAndCallInterfaceMethod.kt");
}
@TestMetadata("correctBoxingForBranchExpressions.kt")
public void testCorrectBoxingForBranchExpressions() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/correctBoxingForBranchExpressions.kt");
@@ -10831,6 +10831,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/inlineClasses/computablePropertyInsideInlineClass.kt");
}
@TestMetadata("conformToComparableAndCallInterfaceMethod.kt")
public void testConformToComparableAndCallInterfaceMethod() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/conformToComparableAndCallInterfaceMethod.kt");
}
@TestMetadata("correctBoxingForBranchExpressions.kt")
public void testCorrectBoxingForBranchExpressions() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/correctBoxingForBranchExpressions.kt");