From 059f1e9eedd69a1b3d89d14c3a98b86b81679b32 Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Wed, 7 Feb 2018 15:39:21 +0300 Subject: [PATCH] Support inline classes working with collection elements (get/set) --- .../kotlin/codegen/ExpressionCodegen.java | 11 +++++-- .../jetbrains/kotlin/codegen/StackValue.java | 14 +++++--- ...oxUnboxInlineClassesWithOperatorsGetSet.kt | 32 +++++++++++++++++++ .../inlineClasses/uIntArraySwapBoxing.kt | 23 +++++++++++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 6 ++++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 ++++ .../codegen/BytecodeTextTestGenerated.java | 6 ++++ .../LightAnalysisModeTestGenerated.java | 6 ++++ .../semantics/JsCodegenBoxTestGenerated.java | 6 ++++ 9 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 compiler/testData/codegen/box/inlineClasses/boxUnboxInlineClassesWithOperatorsGetSet.kt create mode 100644 compiler/testData/codegen/bytecodeText/inlineClasses/uIntArraySwapBoxing.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 4b39a799977..6ee27e4a2ce 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -4050,9 +4050,11 @@ public class ExpressionCodegen extends KtVisitor impleme List indices = expression.getIndexExpressions(); FunctionDescriptor operationDescriptor = (FunctionDescriptor) bindingContext.get(REFERENCE_TARGET, expression); assert operationDescriptor != null; + boolean isInlineClassType = type != null && InlineClassesUtilsKt.isInlineClassType(type); if (arrayType.getSort() == Type.ARRAY && indices.size() == 1 && - isInt(operationDescriptor.getValueParameters().get(0).getType())) { + isInt(operationDescriptor.getValueParameters().get(0).getType()) && + !isInlineClassType) { assert type != null; Type elementType; if (KotlinBuiltIns.isArray(type)) { @@ -4084,7 +4086,12 @@ public class ExpressionCodegen extends KtVisitor impleme ); Type elementType = isGetter ? callableMethod.getReturnType() : ArrayUtil.getLastElement(argumentTypes); - return StackValue.collectionElement(collectionElementReceiver, elementType, resolvedGetCall, resolvedSetCall, this); + KotlinType elementKotlinType = isGetter ? + operationDescriptor.getReturnType() : + CollectionsKt.last(operationDescriptor.getValueParameters()).getType(); + return StackValue.collectionElement( + collectionElementReceiver, elementType, elementKotlinType, resolvedGetCall, resolvedSetCall, this + ); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index 2e2ebee63ff..103b538fd94 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.codegen; import com.intellij.psi.tree.IElementType; import kotlin.Unit; import kotlin.collections.ArraysKt; +import kotlin.collections.CollectionsKt; import kotlin.jvm.functions.Function1; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -282,11 +283,12 @@ public abstract class StackValue { public static StackValue collectionElement( CollectionElementReceiver collectionElementReceiver, Type type, + KotlinType kotlinType, ResolvedCall getter, ResolvedCall setter, ExpressionCodegen codegen ) { - return new CollectionElement(collectionElementReceiver, type, getter, setter, codegen); + return new CollectionElement(collectionElementReceiver, type, kotlinType, getter, setter, codegen); } @NotNull @@ -1167,11 +1169,12 @@ public abstract class StackValue { public CollectionElement( @NotNull CollectionElementReceiver collectionElementReceiver, @NotNull Type type, + @Nullable KotlinType kotlinType, @Nullable ResolvedCall resolvedGetCall, @Nullable ResolvedCall resolvedSetCall, @NotNull ExpressionCodegen codegen ) { - super(type, null, false, false, collectionElementReceiver, true); + super(type, kotlinType, false, false, collectionElementReceiver, true); this.resolvedGetCall = resolvedGetCall; this.resolvedSetCall = resolvedSetCall; this.setter = resolvedSetCall == null ? null : @@ -1246,13 +1249,16 @@ public abstract class StackValue { } @Override - public void storeSelector(@NotNull Type topOfStackType, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) { + public void storeSelector(@NotNull Type topOfStackType, @Nullable KotlinType topOfStackKotlinType, @NotNull InstructionAdapter v) { if (setter == null) { throw new UnsupportedOperationException("no setter specified"); } Type lastParameterType = ArraysKt.last(setter.getParameterTypes()); - coerce(topOfStackType, lastParameterType, v); + KotlinType lastParameterKotlinType = + CollectionsKt.last(resolvedSetCall.getResultingDescriptor().getValueParameters()).getType(); + + coerce(topOfStackType, topOfStackKotlinType, lastParameterType, lastParameterKotlinType, v); getCallGenerator().putValueIfNeeded(lastParameterType, StackValue.onStack(lastParameterType)); diff --git a/compiler/testData/codegen/box/inlineClasses/boxUnboxInlineClassesWithOperatorsGetSet.kt b/compiler/testData/codegen/box/inlineClasses/boxUnboxInlineClassesWithOperatorsGetSet.kt new file mode 100644 index 00000000000..b5d071cbb02 --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/boxUnboxInlineClassesWithOperatorsGetSet.kt @@ -0,0 +1,32 @@ +// !LANGUAGE: +InlineClasses + +inline class UInt(private val value: Int) { + fun asInt() = value +} + +inline class UIntArray(private val intArray: IntArray) { + operator fun get(index: Int): UInt = UInt(intArray[index]) + + operator fun set(index: Int, value: UInt) { + intArray[index] = value.asInt() + } +} + +fun UIntArray.swap(i: Int, j: Int) { + this[j] = this[i].also { this[i] = this[j] } +} + +fun uIntArrayOf(vararg elements: Int) = UIntArray(intArrayOf(*elements)) + +fun box(): String { + val a = uIntArrayOf(1, 2, 3, 4) + a.swap(0, 3) + a.swap(1, 2) + + if (a[0].asInt() != 4) return "fail" + if (a[1].asInt() != 3) return "fail" + if (a[2].asInt() != 2) return "fail" + if (a[3].asInt() != 1) return "fail" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/uIntArraySwapBoxing.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/uIntArraySwapBoxing.kt new file mode 100644 index 00000000000..4467a309403 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/uIntArraySwapBoxing.kt @@ -0,0 +1,23 @@ +// !LANGUAGE: +InlineClasses + +inline class UInt(private val value: Int) { + fun asInt() = value +} + +inline class UIntArray(private val intArray: IntArray) { + operator fun get(index: Int): UInt = UInt(intArray[index]) + + operator fun set(index: Int, value: UInt) { + intArray[index] = value.asInt() + } +} + +fun UIntArray.swap(i: Int, j: Int) { + this[j] = this[i].also { this[i] = this[j] } +} + +// 2 INVOKEVIRTUAL UInt.unbox +// 1 INVOKESTATIC UInt\$Erased.box + +// 0 intValue +// 0 valueOf \ 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 a0d72fc9946..4a731a93ce9 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 @@ -10389,6 +10389,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("boxUnboxInlineClassesWithOperatorsGetSet.kt") + public void testBoxUnboxInlineClassesWithOperatorsGetSet() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/boxUnboxInlineClassesWithOperatorsGetSet.kt"); + doTest(fileName); + } + @TestMetadata("boxUnboxOfInlineClassForCapturedVars.kt") public void testBoxUnboxOfInlineClassForCapturedVars() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/boxUnboxOfInlineClassForCapturedVars.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index e7ada07ffe9..e5e7623d786 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -10389,6 +10389,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("boxUnboxInlineClassesWithOperatorsGetSet.kt") + public void testBoxUnboxInlineClassesWithOperatorsGetSet() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/boxUnboxInlineClassesWithOperatorsGetSet.kt"); + doTest(fileName); + } + @TestMetadata("boxUnboxOfInlineClassForCapturedVars.kt") public void testBoxUnboxOfInlineClassForCapturedVars() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/boxUnboxOfInlineClassForCapturedVars.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index f01dd56fb92..855d4abec24 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1968,6 +1968,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { doTest(fileName); } + @TestMetadata("uIntArraySwapBoxing.kt") + public void testUIntArraySwapBoxing() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/uIntArraySwapBoxing.kt"); + doTest(fileName); + } + @TestMetadata("unboxInlineClassAfterElvis.kt") public void testUnboxInlineClassAfterElvis() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/unboxInlineClassAfterElvis.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 6d94de36155..cbc409ef7eb 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -10389,6 +10389,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("boxUnboxInlineClassesWithOperatorsGetSet.kt") + public void testBoxUnboxInlineClassesWithOperatorsGetSet() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/boxUnboxInlineClassesWithOperatorsGetSet.kt"); + doTest(fileName); + } + @TestMetadata("boxUnboxOfInlineClassForCapturedVars.kt") public void testBoxUnboxOfInlineClassForCapturedVars() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/boxUnboxOfInlineClassForCapturedVars.kt"); 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 8d82a701ca1..6b3b95fc024 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 @@ -11373,6 +11373,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("boxUnboxInlineClassesWithOperatorsGetSet.kt") + public void testBoxUnboxInlineClassesWithOperatorsGetSet() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/boxUnboxInlineClassesWithOperatorsGetSet.kt"); + doTest(fileName); + } + @TestMetadata("boxUnboxOfInlineClassForCapturedVars.kt") public void testBoxUnboxOfInlineClassForCapturedVars() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/boxUnboxOfInlineClassForCapturedVars.kt");