From 1775f294f4f50a7db4b406236ff70626ad538739 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Mon, 11 Dec 2017 12:00:42 +0300 Subject: [PATCH] Intrinsics for 'reversed': infrastructure & primitive range support #KT-21323 In Progress --- .../kotlin/codegen/RangeCodegenUtil.kt | 8 +++++ .../PrimitiveNumberRangeLiteralRangeValue.kt | 7 +++- .../kotlin/codegen/range/RangeValue.kt | 5 +++ .../kotlin/codegen/range/RangeValues.kt | 9 +++++ .../codegen/range/ReversedRangeValue.kt | 34 +++++++++++++++++++ .../forInReversedEmptyRangeLiteral.kt | 14 ++++++++ .../forInReversedRangeLiteral.kt | 24 +++++++++++++ .../forInReversedEmptyRangeLiteral.kt | 19 +++++++++++ .../forInReversedRangeLiteral.kt | 29 ++++++++++++++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 21 ++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 21 ++++++++++++ .../codegen/BytecodeTextTestGenerated.java | 21 ++++++++++++ .../LightAnalysisModeTestGenerated.java | 21 ++++++++++++ .../semantics/JsCodegenBoxTestGenerated.java | 21 ++++++++++++ 14 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/range/ReversedRangeValue.kt create mode 100644 compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.kt create mode 100644 compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedEmptyRangeLiteral.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/RangeCodegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/RangeCodegenUtil.kt index 0e1c0876383..ae660eee755 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/RangeCodegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/RangeCodegenUtil.kt @@ -188,6 +188,14 @@ fun isPrimitiveNumberRangeExtensionContainsPrimitiveNumber(descriptor: CallableD return true } +fun isPrimitiveProgressionReverse(descriptor: CallableDescriptor): Boolean { + if (!isTopLevelInPackage(descriptor, "reversed", "kotlin.ranges")) return false + if (descriptor.valueParameters.isNotEmpty()) return false + val extensionReceiverType = descriptor.extensionReceiverParameter?.type ?: return false + if (!isPrimitiveProgression(extensionReceiverType)) return false + return true +} + private fun isPrimitiveNumberType(type: KotlinType) = KotlinBuiltIns.isByte(type) || KotlinBuiltIns.isShort(type) || diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/PrimitiveNumberRangeLiteralRangeValue.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/PrimitiveNumberRangeLiteralRangeValue.kt index 220ff14603f..d36e7b49368 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/PrimitiveNumberRangeLiteralRangeValue.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/PrimitiveNumberRangeLiteralRangeValue.kt @@ -33,7 +33,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs class PrimitiveNumberRangeLiteralRangeValue( rangeCall: ResolvedCall -): PrimitiveNumberRangeIntrinsicRangeValue(rangeCall) { +) : PrimitiveNumberRangeIntrinsicRangeValue(rangeCall), + ReversableRangeValue { override fun getBoundedValue(codegen: ExpressionCodegen) = SimpleBoundedValue(codegen, rangeCall) @@ -42,6 +43,10 @@ class PrimitiveNumberRangeLiteralRangeValue( getConstRangeForInRangeLiteralGenerator(codegen, forExpression) ?: ForInSimpleProgressionLoopGenerator.fromBoundedValueWithStep1(codegen, forExpression, getBoundedValue(codegen)) + override fun createForInReversedLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator = + // TODO const-bounded version + ForInSimpleProgressionLoopGenerator.fromBoundedValueWithStepMinus1(codegen, forExpression, getBoundedValue(codegen)) + private fun getConstRangeForInRangeLiteralGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator? { val rhsExpression = rangeCall.valueArgumentsByIndex?.run { get(0).arguments[0].getArgumentExpression() } ?: return null val constValue = codegen.getCompileTimeConstant(rhsExpression).safeAs>() ?: return null diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValue.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValue.kt index 54fb1f31d0a..202705b82c8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValue.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValue.kt @@ -32,6 +32,11 @@ interface RangeValue { } +interface ReversableRangeValue : RangeValue { + fun createForInReversedLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator +} + + interface BoundedValue { val instanceType: Type diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValues.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValues.kt index dcc91f4d0ee..a1607bf35c7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValues.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValues.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.jvm.AsmTypes +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.org.objectweb.asm.Type @@ -127,7 +128,15 @@ private fun ExpressionCodegen.createIntrinsifiedRangeValueOrNull(rangeCall: Reso CharSequenceIndicesRangeValue(rangeCall) isComparableRangeTo(rangeCallee) -> ComparableRangeLiteralRangeValue(this, rangeCall) + isPrimitiveProgressionReverse(rangeCallee) -> + createReversedRangeValueOrNull(rangeCall) else -> null } +} + +private fun ExpressionCodegen.createReversedRangeValueOrNull(rangeCall: ResolvedCall): RangeValue? { + val receiver = rangeCall.extensionReceiver as? ExpressionReceiver ?: return null + val receiverRangeValue = createRangeValueForExpression(receiver.expression) as? ReversableRangeValue ?: return null + return ReversedRangeValue(receiverRangeValue) } \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/ReversedRangeValue.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/ReversedRangeValue.kt new file mode 100644 index 00000000000..d5c0b4f9edb --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/ReversedRangeValue.kt @@ -0,0 +1,34 @@ +/* + * 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.codegen.range + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.range.forLoop.ForLoopGenerator +import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.kotlin.psi.KtSimpleNameExpression + +class ReversedRangeValue(private val original: ReversableRangeValue) : RangeValue, ReversableRangeValue { + override fun createInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression): InExpressionGenerator = + original.createInExpressionGenerator(codegen, operatorReference) + + override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator = + original.createForInReversedLoopGenerator(codegen, forExpression) + + override fun createForInReversedLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator = + original.createForLoopGenerator(codegen, forExpression) +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.kt b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.kt new file mode 100644 index 00000000000..683b9365d58 --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME + +fun box(): String { + for (i in (4 .. 1).reversed()) { + throw AssertionError("Loop should not be executed") + } + for (i in (4L .. 1L).reversed()) { + throw AssertionError("Loop should not be executed") + } + for (i in ('D' .. 'A').reversed()) { + throw AssertionError("Loop should not be executed") + } + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt new file mode 100644 index 00000000000..8a9e9f43cab --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt @@ -0,0 +1,24 @@ +// WITH_RUNTIME +import kotlin.test.* + +fun box(): String { + var sum = 0 + for (i in (1 .. 4).reversed()) { + sum = sum * 10 + i + } + assertEquals(4321, sum) + + var sumL = 0L + for (i in (1L .. 4L).reversed()) { + sumL = sumL * 10 + i + } + assertEquals(4321L, sumL) + + var sumC = 0 + for (i in ('1' .. '4').reversed()) { + sumC = sumC * 10 + i.toInt() - '0'.toInt() + } + assertEquals(4321, sumC) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedEmptyRangeLiteral.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedEmptyRangeLiteral.kt new file mode 100644 index 00000000000..3ad62f08326 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedEmptyRangeLiteral.kt @@ -0,0 +1,19 @@ +// WITH_RUNTIME + +fun box(): String { + for (i in (4 .. 1).reversed()) { + throw AssertionError("Loop should not be executed") + } + for (i in (4L .. 1L).reversed()) { + throw AssertionError("Loop should not be executed") + } + for (i in ('D' .. 'A').reversed()) { + throw AssertionError("Loop should not be executed") + } + return "OK" +} + +// 0 reversed +// 0 getFirst +// 0 getLast +// 0 getStep \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt new file mode 100644 index 00000000000..7b0b40f717e --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt @@ -0,0 +1,29 @@ +// WITH_RUNTIME +import kotlin.test.* + +fun box(): String { + var sum = 0 + for (i in (1 .. 4).reversed()) { + sum = sum * 10 + i + } + assertEquals(4321, sum) + + var sumL = 0L + for (i in (1L .. 4L).reversed()) { + sumL = sumL * 10 + i + } + assertEquals(4321L, sumL) + + var sumC = 0 + for (i in ('1' .. '4').reversed()) { + sumC = sumC * 10 + i.toInt() - '0'.toInt() + } + assertEquals(4321, sumC) + + return "OK" +} + +// 0 reversed +// 0 getFirst +// 0 getLast +// 0 getStep \ 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 799c963df13..f19ed5c998d 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 @@ -15233,6 +15233,27 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } } + @TestMetadata("compiler/testData/codegen/box/ranges/forInReversed") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ForInReversed extends AbstractIrBlackBoxCodegenTest { + public void testAllFilesPresentInForInReversed() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("forInReversedEmptyRangeLiteral.kt") + public void testForInReversedEmptyRangeLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.kt"); + doTest(fileName); + } + + @TestMetadata("forInReversedRangeLiteral.kt") + public void testForInReversedRangeLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/ranges/forInUntil") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index fea4dc37295..a48d726e2c3 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -15233,6 +15233,27 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/ranges/forInReversed") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ForInReversed extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInForInReversed() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("forInReversedEmptyRangeLiteral.kt") + public void testForInReversedEmptyRangeLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.kt"); + doTest(fileName); + } + + @TestMetadata("forInReversedRangeLiteral.kt") + public void testForInReversedRangeLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/ranges/forInUntil") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index d5ab0cee710..f84146a63cc 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1399,6 +1399,27 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { doTest(fileName); } } + + @TestMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInReversed") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ForInReversed extends AbstractBytecodeTextTest { + public void testAllFilesPresentInForInReversed() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/forLoop/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("forInReversedEmptyRangeLiteral.kt") + public void testForInReversedEmptyRangeLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedEmptyRangeLiteral.kt"); + doTest(fileName); + } + + @TestMetadata("forInReversedRangeLiteral.kt") + public void testForInReversedRangeLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/codegen/bytecodeText/hashCode") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index b20e01a2b27..918f8c13d13 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -15233,6 +15233,27 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes } } + @TestMetadata("compiler/testData/codegen/box/ranges/forInReversed") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ForInReversed extends AbstractLightAnalysisModeTest { + public void testAllFilesPresentInForInReversed() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("forInReversedEmptyRangeLiteral.kt") + public void testForInReversedEmptyRangeLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.kt"); + doTest(fileName); + } + + @TestMetadata("forInReversedRangeLiteral.kt") + public void testForInReversedRangeLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/ranges/forInUntil") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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 2a7dde33631..316a3bf19c3 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 @@ -16649,6 +16649,27 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/ranges/forInReversed") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ForInReversed extends AbstractJsCodegenBoxTest { + public void testAllFilesPresentInForInReversed() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("forInReversedEmptyRangeLiteral.kt") + public void testForInReversedEmptyRangeLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.kt"); + doTest(fileName); + } + + @TestMetadata("forInReversedRangeLiteral.kt") + public void testForInReversedRangeLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/ranges/forInUntil") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)