diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt index 5df37e36c4f..8a542f4c99d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt @@ -47,6 +47,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils.isSubclass import org.jetbrains.kotlin.resolve.annotations.hasJvmStaticAnnotation import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore +import org.jetbrains.kotlin.resolve.calls.callUtil.getFirstArgumentExpression import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns @@ -420,11 +421,11 @@ fun extractReificationArgument(type: KotlinType): Pair): StackValue = - generateReceiverValue(rangeCall.extensionReceiver ?: rangeCall.dispatchReceiver!!, false) +fun ExpressionCodegen.generateCallReceiver(call: ResolvedCall): StackValue = + generateReceiverValue(call.extensionReceiver ?: call.dispatchReceiver!!, false) -fun ExpressionCodegen.generateCallSingleArgument(rangeCall: ResolvedCall): StackValue = - gen(ExpressionCodegen.getSingleArgumentExpression(rangeCall)!!) +fun ExpressionCodegen.generateCallSingleArgument(call: ResolvedCall): StackValue = + gen(call.getFirstArgumentExpression()!!) fun ClassDescriptor.isPossiblyUninitializedSingleton() = DescriptorUtils.isEnumEntry(this) || diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/DownToProgressionRangeValue.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/DownToProgressionRangeValue.kt index 0414e0b8eb2..3c11fea4cd5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/DownToProgressionRangeValue.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/DownToProgressionRangeValue.kt @@ -20,8 +20,11 @@ import org.jetbrains.kotlin.codegen.ExpressionCodegen import org.jetbrains.kotlin.codegen.generateCallReceiver import org.jetbrains.kotlin.codegen.generateCallSingleArgument import org.jetbrains.kotlin.codegen.range.forLoop.ForInSimpleProgressionLoopGenerator +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.callUtil.getFirstArgumentExpression +import org.jetbrains.kotlin.resolve.calls.callUtil.getReceiverExpression import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall class DownToProgressionRangeValue(rangeCall: ResolvedCall) : @@ -35,8 +38,36 @@ class DownToProgressionRangeValue(rangeCall: ResolvedCall @@ -47,7 +47,7 @@ class PrimitiveNumberRangeLiteralRangeValue( codegen: ExpressionCodegen, forExpression: KtForExpression ): ForLoopGenerator? { - val endExpression = rangeCall.valueArgumentsByIndex?.run { get(0).arguments[0].getArgumentExpression() } ?: return null + val endExpression = rangeCall.getFirstArgumentExpression() ?: return null return createConstBoundedForLoopGeneratorOrNull( codegen, forExpression, codegen.generateCallReceiver(rangeCall), @@ -60,7 +60,7 @@ class PrimitiveNumberRangeLiteralRangeValue( codegen: ExpressionCodegen, forExpression: KtForExpression ): ForLoopGenerator? { - val endExpression = rangeCall.extensionReceiver.safeAs()?.expression ?: return null + val endExpression = rangeCall.getReceiverExpression() ?: return null return createConstBoundedForLoopGeneratorOrNull( codegen, forExpression, codegen.generateCallSingleArgument(rangeCall), diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/callUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/callUtil.kt index ce22d7cb5d6..a2cef014854 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/callUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/callUtil.kt @@ -28,8 +28,10 @@ import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver import org.jetbrains.kotlin.resolve.calls.CallTransformer import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.isError +import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.sure // resolved call @@ -256,3 +258,10 @@ fun Call.createLookupLocation(): KotlinLookupLocation { else callElement return KotlinLookupLocation(element) } + +fun ResolvedCall<*>.getFirstArgumentExpression(): KtExpression? = + valueArgumentsByIndex?.run { get(0).arguments[0].getArgumentExpression() } + +fun ResolvedCall<*>.getReceiverExpression(): KtExpression? = + extensionReceiver.safeAs()?.expression ?: + dispatchReceiver.safeAs()?.expression \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownToWithNonConstBounds.kt b/compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownToWithNonConstBounds.kt new file mode 100644 index 00000000000..1141a4d68c6 --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownToWithNonConstBounds.kt @@ -0,0 +1,16 @@ +// WITH_RUNTIME + +import kotlin.test.assertEquals + +fun low() = 4 +fun high() = 1 + +fun box(): String { + var sum = 0 + for (i in low() downTo high()) { + sum = sum * 10 + i + } + assertEquals(4321, sum) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToCharMinValue.kt b/compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToCharMinValue.kt new file mode 100644 index 00000000000..4af3c19ea1b --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToCharMinValue.kt @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +const val M = 0.toChar() + +fun box(): String { + var count = 0 + for (i in M downTo 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/forWithPossibleOverflow/forInDownToIntMinValue.kt b/compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToIntMinValue.kt new file mode 100644 index 00000000000..901769c7ef4 --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToIntMinValue.kt @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +const val M = Int.MIN_VALUE + +fun box(): String { + var count = 0 + for (i in M downTo 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/forWithPossibleOverflow/forInDownToLongMinValue.kt b/compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToLongMinValue.kt new file mode 100644 index 00000000000..451ba31d1db --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToLongMinValue.kt @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +const val M = Long.MIN_VALUE + +fun box(): String { + var count = 0 + for (i in M downTo 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/forInReversed/forInReversedRangeLiteral.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt index 5ca700af3b5..07f26f41983 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt @@ -29,4 +29,7 @@ fun box(): String { // 0 getFirst // 0 getLast // 0 getStep -// 0 IF_ICMPEQ \ No newline at end of file +// 0 IFEQ +// 0 IF_ICMPEQ +// 2 IF_ICMPLT +// 1 LCMP \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forIntInDownTo.kt b/compiler/testData/codegen/bytecodeText/forLoop/forIntInDownTo.kt index 6442e90b54d..1c73597d72b 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forIntInDownTo.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forIntInDownTo.kt @@ -12,4 +12,6 @@ fun test(): Int { // 0 getStart // 0 getEnd // 0 getFirst -// 0 getLast \ No newline at end of file +// 0 getLast +// 0 IF_ICMPEQ +// 1 IF_ICMPLT \ 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 8727c2a6f59..09e06723284 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 @@ -15103,6 +15103,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("forIntInDownToWithNonConstBounds.kt") + public void testForIntInDownToWithNonConstBounds() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownToWithNonConstBounds.kt"); + doTest(fileName); + } + @TestMetadata("forIntInNonOptimizedDownTo.kt") public void testForIntInNonOptimizedDownTo() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInDownTo/forIntInNonOptimizedDownTo.kt"); @@ -15379,6 +15385,24 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forWithPossibleOverflow"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("forInDownToCharMinValue.kt") + public void testForInDownToCharMinValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToCharMinValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInDownToIntMinValue.kt") + public void testForInDownToIntMinValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToIntMinValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInDownToLongMinValue.kt") + public void testForInDownToLongMinValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToLongMinValue.kt"); + doTest(fileName); + } + @TestMetadata("forInRangeToCharMaxValue.kt") public void testForInRangeToCharMaxValue() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToCharMaxValue.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 0f3540f1a6b..1ef9a739d77 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -15103,6 +15103,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("forIntInDownToWithNonConstBounds.kt") + public void testForIntInDownToWithNonConstBounds() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownToWithNonConstBounds.kt"); + doTest(fileName); + } + @TestMetadata("forIntInNonOptimizedDownTo.kt") public void testForIntInNonOptimizedDownTo() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInDownTo/forIntInNonOptimizedDownTo.kt"); @@ -15379,6 +15385,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forWithPossibleOverflow"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("forInDownToCharMinValue.kt") + public void testForInDownToCharMinValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToCharMinValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInDownToIntMinValue.kt") + public void testForInDownToIntMinValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToIntMinValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInDownToLongMinValue.kt") + public void testForInDownToLongMinValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToLongMinValue.kt"); + doTest(fileName); + } + @TestMetadata("forInRangeToCharMaxValue.kt") public void testForInRangeToCharMaxValue() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToCharMaxValue.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index bdd7b9d4d8f..ded0d6e7a01 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -15103,6 +15103,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("forIntInDownToWithNonConstBounds.kt") + public void testForIntInDownToWithNonConstBounds() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownToWithNonConstBounds.kt"); + doTest(fileName); + } + @TestMetadata("forIntInNonOptimizedDownTo.kt") public void testForIntInNonOptimizedDownTo() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInDownTo/forIntInNonOptimizedDownTo.kt"); @@ -15379,6 +15385,24 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forWithPossibleOverflow"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("forInDownToCharMinValue.kt") + public void testForInDownToCharMinValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToCharMinValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInDownToIntMinValue.kt") + public void testForInDownToIntMinValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToIntMinValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInDownToLongMinValue.kt") + public void testForInDownToLongMinValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToLongMinValue.kt"); + doTest(fileName); + } + @TestMetadata("forInRangeToCharMaxValue.kt") public void testForInRangeToCharMaxValue() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToCharMaxValue.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 98b14240de6..467fe7b08a0 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 @@ -16519,6 +16519,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("forIntInDownToWithNonConstBounds.kt") + public void testForIntInDownToWithNonConstBounds() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownToWithNonConstBounds.kt"); + doTest(fileName); + } + @TestMetadata("forIntInNonOptimizedDownTo.kt") public void testForIntInNonOptimizedDownTo() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInDownTo/forIntInNonOptimizedDownTo.kt"); @@ -16795,6 +16801,24 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forWithPossibleOverflow"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("forInDownToCharMinValue.kt") + public void testForInDownToCharMinValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToCharMinValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInDownToIntMinValue.kt") + public void testForInDownToIntMinValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToIntMinValue.kt"); + doTest(fileName); + } + + @TestMetadata("forInDownToLongMinValue.kt") + public void testForInDownToLongMinValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInDownToLongMinValue.kt"); + doTest(fileName); + } + @TestMetadata("forInRangeToCharMaxValue.kt") public void testForInRangeToCharMaxValue() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forWithPossibleOverflow/forInRangeToCharMaxValue.kt");