diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/ArrayRangeValue.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/ArrayRangeValue.kt index 7f8f0bd30db..8fd7f5ebc47 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/ArrayRangeValue.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/ArrayRangeValue.kt @@ -23,9 +23,12 @@ import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator import org.jetbrains.kotlin.psi.KtForExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression -class ArrayRangeValue(private val canCacheArrayLength: Boolean) : RangeValue { +class ArrayRangeValue( + private val canCacheArrayLength: Boolean, + private val shouldAlwaysStoreArrayInNewVar: Boolean +) : RangeValue { override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) = - ForInArrayLoopGenerator(codegen, forExpression, canCacheArrayLength) + ForInArrayLoopGenerator(codegen, forExpression, canCacheArrayLength, shouldAlwaysStoreArrayInNewVar) override fun createInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression): InExpressionGenerator = CallBasedInExpressionGenerator(codegen, operatorReference) 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 e7e1ef72e22..dcc91f4d0ee 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValues.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValues.kt @@ -18,8 +18,10 @@ package org.jetbrains.kotlin.codegen.range import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.* +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor +import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext @@ -46,8 +48,15 @@ fun ExpressionCodegen.createRangeValueForExpression(rangeExpression: KtExpressio val builtIns = state.module.builtIns return when { - asmRangeType.sort == Type.ARRAY -> - ArrayRangeValue(!isLocalVarReference(rangeExpression, bindingContext)) + asmRangeType.sort == Type.ARRAY -> { + val properForInArraySemantics = + state.languageVersionSettings.supportsFeature(LanguageFeature.ProperForInArrayLoopRangeVariableAssignmentSemantic) + ArrayRangeValue( + properForInArraySemantics || !isLocalVarReference(rangeExpression, bindingContext), + properForInArraySemantics + ) + } + isPrimitiveRange(rangeType) -> PrimitiveRangeRangeValue() isPrimitiveProgression(rangeType) -> @@ -64,7 +73,10 @@ fun ExpressionCodegen.createRangeValueForExpression(rangeExpression: KtExpressio fun isLocalVarReference(rangeExpression: KtExpression, bindingContext: BindingContext): Boolean { if (rangeExpression !is KtSimpleNameExpression) return false val resultingDescriptor = rangeExpression.getResolvedCall(bindingContext)?.resultingDescriptor ?: return false - return resultingDescriptor is LocalVariableDescriptor && resultingDescriptor.isVar + return resultingDescriptor is LocalVariableDescriptor && + resultingDescriptor !is SyntheticFieldDescriptor && + !resultingDescriptor.isDelegated && + resultingDescriptor.isVar } private fun isSubtypeOfString(type: KotlinType, builtIns: KotlinBuiltIns) = diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/forLoop/ForInArrayLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/forLoop/ForInArrayLoopGenerator.kt index 513999302b2..e4afb1733c9 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/forLoop/ForInArrayLoopGenerator.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/forLoop/ForInArrayLoopGenerator.kt @@ -29,9 +29,8 @@ import org.jetbrains.org.objectweb.asm.Type class ForInArrayLoopGenerator( codegen: ExpressionCodegen, forExpression: KtForExpression, - // We can cache array length if the corresponding range expression is not a local var. - // See https://youtrack.jetbrains.com/issue/KT-21354. - private val canCacheArrayLength: Boolean + private val canCacheArrayLength: Boolean, + private val shouldAlwaysStoreArrayInNewVar: Boolean ) : AbstractForLoopGenerator(codegen, forExpression) { private var indexVar: Int = 0 private var arrayVar: Int = 0 @@ -46,7 +45,7 @@ class ForInArrayLoopGenerator( val loopRange = forExpression.loopRange val value = codegen.gen(loopRange) val asmLoopRangeType = codegen.asmType(loopRangeType) - if (value is StackValue.Local && value.type == asmLoopRangeType) { + if (!shouldAlwaysStoreArrayInNewVar && value is StackValue.Local && value.type == asmLoopRangeType) { arrayVar = value.index // no need to copy local variable into another variable } else { diff --git a/compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody12.kt b/compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody12.kt new file mode 100644 index 00000000000..55317b7580c --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody12.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME +// LANGUAGE_VERSION: 1.2 +// IGNORE_BACKEND: JS + +fun box(): String { + var xs = intArrayOf(1, 2, 3) + var sum = 0 + for (x in xs) { + sum = sum * 10 + x + xs = intArrayOf(4, 5) + } + return if (sum == 15) "OK" else "Fail: $sum" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody.kt b/compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody13.kt similarity index 91% rename from compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody.kt rename to compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody13.kt index a54592b0fda..6b256bcc1bb 100644 --- a/compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody.kt +++ b/compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody13.kt @@ -1,5 +1,5 @@ // WITH_RUNTIME -// IGNORE_BACKEND: JS +// LANGUAGE_VERSION: 1.3 // In Kotlin 1.0, in a for-in-array loop, range expression is cached in // a local variable unless it is already a local variable. @@ -19,5 +19,5 @@ fun box(): String { sum = sum * 10 + x xs = intArrayOf(4, 5) } - return if (sum == 15) "OK" else "Fail: $sum" + return if (sum == 123) "OK" else "Fail: $sum" } \ 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 fca4815fda0..14fbf38d72b 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 @@ -14555,9 +14555,15 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } - @TestMetadata("forInArrayWithArrayVarUpdatedInLoopBody.kt") - public void testForInArrayWithArrayVarUpdatedInLoopBody() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody.kt"); + @TestMetadata("forInArrayWithArrayVarUpdatedInLoopBody12.kt") + public void testForInArrayWithArrayVarUpdatedInLoopBody12() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody12.kt"); + doTest(fileName); + } + + @TestMetadata("forInArrayWithArrayVarUpdatedInLoopBody13.kt") + public void testForInArrayWithArrayVarUpdatedInLoopBody13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody13.kt"); doTest(fileName); } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index bb4393e198a..0c9b1a55750 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -14555,9 +14555,15 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } - @TestMetadata("forInArrayWithArrayVarUpdatedInLoopBody.kt") - public void testForInArrayWithArrayVarUpdatedInLoopBody() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody.kt"); + @TestMetadata("forInArrayWithArrayVarUpdatedInLoopBody12.kt") + public void testForInArrayWithArrayVarUpdatedInLoopBody12() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody12.kt"); + doTest(fileName); + } + + @TestMetadata("forInArrayWithArrayVarUpdatedInLoopBody13.kt") + public void testForInArrayWithArrayVarUpdatedInLoopBody13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody13.kt"); doTest(fileName); } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 3e409087448..03fb3a59554 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -14555,9 +14555,15 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } - @TestMetadata("forInArrayWithArrayVarUpdatedInLoopBody.kt") - public void testForInArrayWithArrayVarUpdatedInLoopBody() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody.kt"); + @TestMetadata("forInArrayWithArrayVarUpdatedInLoopBody12.kt") + public void testForInArrayWithArrayVarUpdatedInLoopBody12() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody12.kt"); + doTest(fileName); + } + + @TestMetadata("forInArrayWithArrayVarUpdatedInLoopBody13.kt") + public void testForInArrayWithArrayVarUpdatedInLoopBody13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody13.kt"); doTest(fileName); } 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 1ec71fe9208..5220b836dd1 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 @@ -15827,9 +15827,9 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } - @TestMetadata("forInArrayWithArrayVarUpdatedInLoopBody.kt") - public void testForInArrayWithArrayVarUpdatedInLoopBody() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody.kt"); + @TestMetadata("forInArrayWithArrayVarUpdatedInLoopBody12.kt") + public void testForInArrayWithArrayVarUpdatedInLoopBody12() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody12.kt"); try { doTest(fileName); } @@ -15839,6 +15839,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); } + @TestMetadata("forInArrayWithArrayVarUpdatedInLoopBody13.kt") + public void testForInArrayWithArrayVarUpdatedInLoopBody13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInArrayWithArrayVarUpdatedInLoopBody13.kt"); + doTest(fileName); + } + @TestMetadata("forInRangeLiteralWithMixedTypeBounds.kt") public void testForInRangeLiteralWithMixedTypeBounds() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInRangeLiteralWithMixedTypeBounds.kt");