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 d36e7b49368..219116467b0 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/PrimitiveNumberRangeLiteralRangeValue.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/PrimitiveNumberRangeLiteralRangeValue.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.constants.ByteValue import org.jetbrains.kotlin.resolve.constants.IntValue import org.jetbrains.kotlin.resolve.constants.IntegerValueConstant import org.jetbrains.kotlin.resolve.constants.ShortValue +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.utils.addToStdlib.safeAs class PrimitiveNumberRangeLiteralRangeValue( @@ -40,21 +41,24 @@ class PrimitiveNumberRangeLiteralRangeValue( SimpleBoundedValue(codegen, rangeCall) override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator = - getConstRangeForInRangeLiteralGenerator(codegen, forExpression) ?: + createConstBoundedForInRangeLiteralGenerator(codegen, forExpression) ?: ForInSimpleProgressionLoopGenerator.fromBoundedValueWithStep1(codegen, forExpression, getBoundedValue(codegen)) override fun createForInReversedLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator = - // TODO const-bounded version + createConstBoundedRangeForInReversedRangeLiteralGenerator(codegen, forExpression) ?: ForInSimpleProgressionLoopGenerator.fromBoundedValueWithStepMinus1(codegen, forExpression, getBoundedValue(codegen)) - private fun getConstRangeForInRangeLiteralGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator? { + private fun createConstBoundedForInRangeLiteralGenerator( + 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 val untilValue = when (constValue) { is ByteValue -> constValue.value + 1 is ShortValue -> constValue.value + 1 is IntValue -> constValue.value + 1 - else -> return null + else -> Int.MIN_VALUE } // Watch out for integer overflow @@ -70,4 +74,32 @@ class PrimitiveNumberRangeLiteralRangeValue( step = 1 ) } + + private fun createConstBoundedRangeForInReversedRangeLiteralGenerator( + codegen: ExpressionCodegen, + forExpression: KtForExpression + ): ForLoopGenerator? { + val lhsExpression = rangeCall.extensionReceiver.safeAs()?.expression ?: return null + val constValue = codegen.getCompileTimeConstant(lhsExpression).safeAs>() ?: return null + val untilValue = when (constValue) { + is ByteValue -> constValue.value - 1 + is ShortValue -> constValue.value - 1 + is IntValue -> constValue.value - 1 + else -> Int.MAX_VALUE + } + + // Watch out for integer overflow + return if (untilValue == Int.MAX_VALUE) + null + else + ForInSimpleProgressionLoopGenerator( + codegen, forExpression, + startValue = codegen.generateCallSingleArgument(rangeCall), + isStartInclusive = true, + endValue = StackValue.integerConstant(untilValue, asmElementType), + isEndInclusive = false, + step = -1 + ) + } + } \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteralWithNonConstBounds.kt b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteralWithNonConstBounds.kt new file mode 100644 index 00000000000..32d61af04a3 --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteralWithNonConstBounds.kt @@ -0,0 +1,25 @@ +// WITH_RUNTIME +import kotlin.test.* + +fun intLow() = 4 +fun intHigh() = 1 +fun longLow() = 4L +fun longHigh() = 1L +fun charLow() = '4' +fun charHigh() = '1' + +fun box(): String { + for (i in (intLow() .. intHigh()).reversed()) { + throw AssertionError("Loop should not be executed") + } + + for (i in (longLow() .. longHigh()).reversed()) { + throw AssertionError("Loop should not be executed") + } + + for (i in (charLow() .. charHigh()).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/forInReversedRangeLiteralWithNonConstBounds.kt b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt new file mode 100644 index 00000000000..0d85baba426 --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt @@ -0,0 +1,31 @@ +// WITH_RUNTIME +import kotlin.test.* + +fun intLow() = 1 +fun intHigh() = 4 +fun longLow() = 1L +fun longHigh() = 4L +fun charLow() = '1' +fun charHigh() = '4' + +fun box(): String { + var sum = 0 + for (i in (intLow() .. intHigh()).reversed()) { + sum = sum * 10 + i + } + assertEquals(4321, sum) + + var sumL = 0L + for (i in (longLow() .. longHigh()).reversed()) { + sumL = sumL * 10 + i + } + assertEquals(4321L, sumL) + + var sumC = 0 + for (i in (charLow() .. charHigh()).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 index 3ad62f08326..13fe6972ea4 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedEmptyRangeLiteral.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedEmptyRangeLiteral.kt @@ -14,6 +14,8 @@ fun box(): String { } // 0 reversed +// 0 getStart +// 0 getEnd // 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 index 7b0b40f717e..5042a42da7b 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt @@ -24,6 +24,10 @@ fun box(): String { } // 0 reversed +// 0 getStart +// 0 getEnd // 0 getFirst // 0 getLast -// 0 getStep \ No newline at end of file +// 0 getStep +// 2 IF_ICMPEQ +// ^ 1 for char progression, 1 for long progression \ 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 f19ed5c998d..2f474b9a5a4 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 @@ -15247,11 +15247,23 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("forInReversedEmptyRangeLiteralWithNonConstBounds.kt") + public void testForInReversedEmptyRangeLiteralWithNonConstBounds() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteralWithNonConstBounds.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("forInReversedRangeLiteralWithNonConstBounds.kt") + public void testForInReversedRangeLiteralWithNonConstBounds() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/codegen/box/ranges/forInUntil") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index a48d726e2c3..6cee7934d51 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -15247,11 +15247,23 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("forInReversedEmptyRangeLiteralWithNonConstBounds.kt") + public void testForInReversedEmptyRangeLiteralWithNonConstBounds() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteralWithNonConstBounds.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("forInReversedRangeLiteralWithNonConstBounds.kt") + public void testForInReversedRangeLiteralWithNonConstBounds() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/codegen/box/ranges/forInUntil") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 918f8c13d13..460e053a8b8 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -15247,11 +15247,23 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("forInReversedEmptyRangeLiteralWithNonConstBounds.kt") + public void testForInReversedEmptyRangeLiteralWithNonConstBounds() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteralWithNonConstBounds.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("forInReversedRangeLiteralWithNonConstBounds.kt") + public void testForInReversedRangeLiteralWithNonConstBounds() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/codegen/box/ranges/forInUntil") 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 316a3bf19c3..4106939e596 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 @@ -16663,11 +16663,23 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("forInReversedEmptyRangeLiteralWithNonConstBounds.kt") + public void testForInReversedEmptyRangeLiteralWithNonConstBounds() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteralWithNonConstBounds.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("forInReversedRangeLiteralWithNonConstBounds.kt") + public void testForInReversedRangeLiteralWithNonConstBounds() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/codegen/box/ranges/forInUntil")