diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index de1ee92fcbb..2d04a1d72a5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -187,6 +187,9 @@ public abstract class StackValue { else if (type == Type.BYTE_TYPE || type == Type.SHORT_TYPE || type == Type.INT_TYPE) { return constant(Integer.valueOf(value), type); } + else if (type == Type.CHAR_TYPE) { + return constant(Character.valueOf((char) value), type); + } else { throw new AssertionError("Unexpected integer type: " + 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 a3a99004ac1..e4438e56743 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/PrimitiveNumberRangeLiteralRangeValue.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/PrimitiveNumberRangeLiteralRangeValue.kt @@ -27,10 +27,7 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor 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.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.constants.* import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -82,18 +79,61 @@ class PrimitiveNumberRangeLiteralRangeValue( startValue: StackValue, endExpression: KtExpression, step: Int - ) : ForLoopGenerator? { + ): ForLoopGenerator? { val endConstValue = codegen.getCompileTimeConstant(endExpression).safeAs>() ?: return null - val endIntValue = when (endConstValue) { - is ByteValue -> endConstValue.value.toInt() - is ShortValue -> endConstValue.value.toInt() - is IntValue -> endConstValue.value - else -> return null - } - return if (isProhibitedIntConstEndValue(step, endIntValue)) - null - else + return when (endConstValue) { + is ByteValue -> { + val endIntValue = endConstValue.value.toInt() + if (isProhibitedIntConstEndValue(step, endIntValue)) + null + else + createConstBoundedIntForLoopGenerator(codegen, forExpression, startValue, endIntValue, step) + } + + is ShortValue -> { + val endIntValue = endConstValue.value.toInt() + if (isProhibitedIntConstEndValue(step, endIntValue)) + null + else + createConstBoundedIntForLoopGenerator(codegen, forExpression, startValue, endIntValue, step) + } + + is IntValue -> { + val endIntValue = endConstValue.value + if (isProhibitedIntConstEndValue(step, endIntValue)) + null + else + createConstBoundedIntForLoopGenerator(codegen, forExpression, startValue, endIntValue, step) + } + + is CharValue -> { + val endCharValue = endConstValue.value + if (isProhibitedCharConstEndValue(step, endCharValue)) + null + else + createConstBoundedIntForLoopGenerator(codegen, forExpression, startValue, endCharValue.toInt(), step) + } + + is LongValue -> { + val endLongValue = endConstValue.value + if (isProhibitedLongConstEndValue(step, endLongValue)) + null + else + createConstBoundedLongForLoopGenerator(codegen, forExpression, startValue, endLongValue, step) + } + + else -> null + } + } + + private fun createConstBoundedIntForLoopGenerator( + codegen: ExpressionCodegen, + forExpression: KtForExpression, + startValue: StackValue, + endIntValue: Int, + step: Int + ): ForLoopGenerator? = ForInDefinitelySafeSimpleProgressionLoopGenerator( codegen, forExpression, startValue = startValue, @@ -102,9 +142,30 @@ class PrimitiveNumberRangeLiteralRangeValue( isEndInclusive = true, step = step ) - } + + private fun createConstBoundedLongForLoopGenerator( + codegen: ExpressionCodegen, + forExpression: KtForExpression, + startValue: StackValue, + endLongValue: Long, + step: Int + ): ForLoopGenerator? = + ForInDefinitelySafeSimpleProgressionLoopGenerator( + codegen, forExpression, + startValue = startValue, + isStartInclusive = true, + endValue = StackValue.constant(endLongValue, asmElementType), + isEndInclusive = true, + step = step + ) + + private fun isProhibitedCharConstEndValue(step: Int, endValue: Char) = + endValue == if (step == 1) java.lang.Character.MAX_VALUE else java.lang.Character.MIN_VALUE private fun isProhibitedIntConstEndValue(step: Int, endValue: Int) = endValue == if (step == 1) Int.MAX_VALUE else Int.MIN_VALUE + private fun isProhibitedLongConstEndValue(step: Int, endValue: Long) = + endValue == if (step == 1) Long.MAX_VALUE else Long.MIN_VALUE + } \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToCharMaxValue.kt b/compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToCharMaxValue.kt new file mode 100644 index 00000000000..3aba3aa6833 --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToCharMaxValue.kt @@ -0,0 +1,13 @@ +const val M = 0xFFFF.toChar() + +fun box(): String { + var count = 0 + for (i in M .. M) { + ++count + if (count > 1) { + throw AssertionError("Loop should be executed once") + } + } + if (count != 1) throw AssertionError("Should be executed once") + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forInRangeToConstWithOverflow.kt b/compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToIntMaxValue.kt similarity index 100% rename from compiler/testData/codegen/box/ranges/forInRangeToConstWithOverflow.kt rename to compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToIntMaxValue.kt diff --git a/compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToLongMaxValue.kt b/compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToLongMaxValue.kt new file mode 100644 index 00000000000..5f0b3f77c9c --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToLongMaxValue.kt @@ -0,0 +1,13 @@ +const val M = Long.MAX_VALUE + +fun box(): String { + var count = 0 + for (i in M .. M) { + ++count + if (count > 1) { + throw AssertionError("Loop should be executed once") + } + } + if (count != 1) throw AssertionError("Should be executed once") + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharConst.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharConst.kt new file mode 100644 index 00000000000..70137833d1b --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharConst.kt @@ -0,0 +1,12 @@ +const val N = 'Z' + +fun test(): Int { + var sum = 0 + for (i in 'A' .. N) { + sum += i.toInt() + } + return sum +} + +// 0 IF_ICMPEQ +// 1 IF_ICMPGT \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt index 3a73e03cf4d..7a972d1ddbf 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt @@ -9,4 +9,4 @@ fun test(): Int { } // 0 IF_ICMPEQ -// 1 IF_ICMPGE \ No newline at end of file +// 1 IF_ICMPGT \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongConst.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongConst.kt new file mode 100644 index 00000000000..8cb0c87da52 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongConst.kt @@ -0,0 +1,13 @@ +const val N = 42L + +fun test(): Long { + var sum = 0L + for (i in 1L .. N) { + sum += i + } + return sum +} + +// 1 LCMP +// 0 IFEQ +// 1 IFGT \ 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 5042a42da7b..5ca700af3b5 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt @@ -29,5 +29,4 @@ fun box(): String { // 0 getFirst // 0 getLast // 0 getStep -// 2 IF_ICMPEQ -// ^ 1 for char progression, 1 for long progression \ No newline at end of file +// 0 IF_ICMPEQ \ 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 18020a21f3b..8727c2a6f59 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 @@ -14594,12 +14594,6 @@ 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"); @@ -15377,6 +15371,33 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } } + @TestMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ForWithPossibleOverflow extends AbstractIrBlackBoxCodegenTest { + public void testAllFilesPresentInForWithPossibleOverflow() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forWithPossibleOverflow"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("forInRangeToCharMaxValue.kt") + public void testForInRangeToCharMaxValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToCharMaxValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInRangeToIntMaxValue.kt") + public void testForInRangeToIntMaxValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToIntMaxValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInRangeToLongMaxValue.kt") + public void testForInRangeToLongMaxValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToLongMaxValue.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/ranges/literal") @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 265f5b71c4b..0f3540f1a6b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -14594,12 +14594,6 @@ 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"); @@ -15377,6 +15371,33 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ForWithPossibleOverflow extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInForWithPossibleOverflow() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forWithPossibleOverflow"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("forInRangeToCharMaxValue.kt") + public void testForInRangeToCharMaxValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToCharMaxValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInRangeToIntMaxValue.kt") + public void testForInRangeToIntMaxValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToIntMaxValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInRangeToLongMaxValue.kt") + public void testForInRangeToLongMaxValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToLongMaxValue.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/ranges/literal") @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 4c72dc71d2c..98cd3e8b97a 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1289,12 +1289,24 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { doTest(fileName); } + @TestMetadata("forInRangeToCharConst.kt") + public void testForInRangeToCharConst() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharConst.kt"); + doTest(fileName); + } + @TestMetadata("forInRangeToConst.kt") public void testForInRangeToConst() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt"); doTest(fileName); } + @TestMetadata("forInRangeToLongConst.kt") + public void testForInRangeToLongConst() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongConst.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 fc97b506cd9..bdd7b9d4d8f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -14594,12 +14594,6 @@ 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"); @@ -15377,6 +15371,33 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes } } + @TestMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ForWithPossibleOverflow extends AbstractLightAnalysisModeTest { + public void testAllFilesPresentInForWithPossibleOverflow() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forWithPossibleOverflow"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("forInRangeToCharMaxValue.kt") + public void testForInRangeToCharMaxValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToCharMaxValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInRangeToIntMaxValue.kt") + public void testForInRangeToIntMaxValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToIntMaxValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInRangeToLongMaxValue.kt") + public void testForInRangeToLongMaxValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToLongMaxValue.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/ranges/literal") @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 90be42ef6e5..98b14240de6 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 @@ -15872,12 +15872,6 @@ 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"); @@ -16793,6 +16787,33 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ForWithPossibleOverflow extends AbstractJsCodegenBoxTest { + public void testAllFilesPresentInForWithPossibleOverflow() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forWithPossibleOverflow"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("forInRangeToCharMaxValue.kt") + public void testForInRangeToCharMaxValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToCharMaxValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInRangeToIntMaxValue.kt") + public void testForInRangeToIntMaxValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToIntMaxValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInRangeToLongMaxValue.kt") + public void testForInRangeToLongMaxValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToLongMaxValue.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/ranges/literal") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)