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 141b8fc74dd..8e4ee2b1a54 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/PrimitiveNumberRangeLiteralRangeValue.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/PrimitiveNumberRangeLiteralRangeValue.kt @@ -18,14 +18,33 @@ package org.jetbrains.kotlin.codegen.range import org.jetbrains.kotlin.codegen.ExpressionCodegen import org.jetbrains.kotlin.codegen.range.forLoop.ForInRangeLiteralLoopGenerator +import org.jetbrains.kotlin.codegen.range.forLoop.ForInUntilConstantRangeLoopGenerator +import org.jetbrains.kotlin.codegen.range.forLoop.ForLoopGenerator import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.psi.KtForExpression import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.constants.* +import org.jetbrains.kotlin.utils.addToStdlib.safeAs class PrimitiveNumberRangeLiteralRangeValue(rangeCall: ResolvedCall): PrimitiveNumberRangeIntrinsicRangeValue(rangeCall) { override fun getBoundedValue(codegen: ExpressionCodegen) = SimpleBoundedValue(codegen, rangeCall) - override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) = + override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator = + getConstRangeForInRangeLiteralGenerator(codegen, forExpression) ?: ForInRangeLiteralLoopGenerator(codegen, forExpression, rangeCall) + + 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 + val untilValue = when (constValue) { + is ByteValue -> constValue.value + 1 + is ShortValue -> constValue.value + 1 + is IntValue -> constValue.value + 1 + else -> return null + } + // Watch out for integer overflow + if (untilValue == Int.MIN_VALUE) return null + return ForInUntilConstantRangeLoopGenerator(codegen, forExpression, rangeCall, untilValue) + } } \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/forLoop/ForInUntilConstantRangeLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/forLoop/ForInUntilConstantRangeLoopGenerator.kt new file mode 100644 index 00000000000..37d870157bb --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/forLoop/ForInUntilConstantRangeLoopGenerator.kt @@ -0,0 +1,40 @@ +/* + * 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.forLoop + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.constants.* +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue + +class ForInUntilConstantRangeLoopGenerator( + codegen: ExpressionCodegen, + forExpression: KtForExpression, + loopRangeCall: ResolvedCall<*>, + private val untilValue: Int +) : AbstractForInExclusiveRangeLoopGenerator(codegen, forExpression) { + private val from: ReceiverValue = loopRangeCall.dispatchReceiver!! + + override fun generateFrom(): StackValue = + codegen.generateReceiverValue(from, false) + + override fun generateTo(): StackValue = + StackValue.constant(untilValue, asmElementType) +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forInRangeToConstWithOverflow.kt b/compiler/testData/codegen/box/ranges/forInRangeToConstWithOverflow.kt new file mode 100644 index 00000000000..b5cce712005 --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInRangeToConstWithOverflow.kt @@ -0,0 +1,12 @@ +const val M = Int.MAX_VALUE + +fun box(): String { + var step = 0 + for (i in M .. M) { + ++step + if (step > 1) throw AssertionError("Should be executed once") + } + if (step != 1) throw AssertionError("Should be executed once") + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt new file mode 100644 index 00000000000..3a73e03cf4d --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt @@ -0,0 +1,12 @@ +const val N = 42 + +fun test(): Int { + var sum = 0 + for (i in 1 .. N) { + sum += i + } + return sum +} + +// 0 IF_ICMPEQ +// 1 IF_ICMPGE \ 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 29197d307df..e5f3b0eaf2b 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 @@ -13322,6 +13322,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("forInRangeToConstWithOverflow.kt") + public void testForInRangeToConstWithOverflow() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInRangeToConstWithOverflow.kt"); + doTest(fileName); + } + @TestMetadata("forInRangeWithImplicitReceiver.kt") public void testForInRangeWithImplicitReceiver() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInRangeWithImplicitReceiver.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index b700f8aeb02..71fd3e1acb4 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -13322,6 +13322,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("forInRangeToConstWithOverflow.kt") + public void testForInRangeToConstWithOverflow() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInRangeToConstWithOverflow.kt"); + doTest(fileName); + } + @TestMetadata("forInRangeWithImplicitReceiver.kt") public void testForInRangeWithImplicitReceiver() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInRangeWithImplicitReceiver.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 463179ed93a..c37a8d1df9c 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1238,6 +1238,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { doTest(fileName); } + @TestMetadata("forInRangeToConst.kt") + public void testForInRangeToConst() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt"); + doTest(fileName); + } + @TestMetadata("forInRangeWithImplicitReceiver.kt") public void testForInRangeWithImplicitReceiver() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInRangeWithImplicitReceiver.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index b686b931ba8..de9255e4cff 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -13322,6 +13322,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("forInRangeToConstWithOverflow.kt") + public void testForInRangeToConstWithOverflow() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInRangeToConstWithOverflow.kt"); + doTest(fileName); + } + @TestMetadata("forInRangeWithImplicitReceiver.kt") public void testForInRangeWithImplicitReceiver() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInRangeWithImplicitReceiver.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 83686b2391d..1cf67be2d5b 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 @@ -14942,6 +14942,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("forInRangeToConstWithOverflow.kt") + public void testForInRangeToConstWithOverflow() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInRangeToConstWithOverflow.kt"); + doTest(fileName); + } + @TestMetadata("forInRangeWithImplicitReceiver.kt") public void testForInRangeWithImplicitReceiver() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInRangeWithImplicitReceiver.kt");