diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java index bad5caf203f..7c9f2d7c7ed 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java @@ -2772,6 +2772,70 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest { } } + @Nested + @TestMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInRangeUntil") + @TestDataPath("$PROJECT_ROOT") + public class ForInRangeUntil { + @Test + public void testAllFilesPresentInForInRangeUntil() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/forLoop/forInRangeUntil"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("forInRangeUntilChar.kt") + public void testForInRangeUntilChar() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeUntil/forInRangeUntilChar.kt"); + } + + @Test + @TestMetadata("forInRangeUntilCharMaxValue.kt") + public void testForInRangeUntilCharMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeUntil/forInRangeUntilCharMaxValue.kt"); + } + + @Test + @TestMetadata("forInRangeUntilCharMinValue.kt") + public void testForInRangeUntilCharMinValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeUntil/forInRangeUntilCharMinValue.kt"); + } + + @Test + @TestMetadata("forInRangeUntilInt.kt") + public void testForInRangeUntilInt() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeUntil/forInRangeUntilInt.kt"); + } + + @Test + @TestMetadata("forInRangeUntilIntMaxValue.kt") + public void testForInRangeUntilIntMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeUntil/forInRangeUntilIntMaxValue.kt"); + } + + @Test + @TestMetadata("forInRangeUntilIntMinValue.kt") + public void testForInRangeUntilIntMinValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeUntil/forInRangeUntilIntMinValue.kt"); + } + + @Test + @TestMetadata("forInRangeUntilLong.kt") + public void testForInRangeUntilLong() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeUntil/forInRangeUntilLong.kt"); + } + + @Test + @TestMetadata("forInRangeUntilLongMaxValue.kt") + public void testForInRangeUntilLongMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeUntil/forInRangeUntilLongMaxValue.kt"); + } + + @Test + @TestMetadata("forInRangeUntilLongMinValue.kt") + public void testForInRangeUntilLongMinValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeUntil/forInRangeUntilLongMinValue.kt"); + } + } + @Nested @TestMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInRangeWithUpperBoundMinus1") @TestDataPath("$PROJECT_ROOT") @@ -5224,6 +5288,12 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/ranges/inOptimizableUnsignedRange.kt"); } + @Test + @TestMetadata("inRangeUntil.kt") + public void testInRangeUntil() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/ranges/inRangeUntil.kt"); + } + @Test @TestMetadata("inUntil.kt") public void testInUntil() throws Exception { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/RangeContainsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/RangeContainsLowering.kt index b9335a05730..eb57086c52c 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/RangeContainsLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/RangeContainsLowering.kt @@ -393,6 +393,7 @@ internal open class RangeHeaderInfoBuilder(context: CommonBackendContext, scopeO ArrayIndicesHandler(context), CharSequenceIndicesHandler(context), UntilHandler(context), + RangeUntilHandler(context), DownToHandler(context), RangeToHandler(context) ) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt index 3e1924c9079..14beece0beb 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt @@ -79,7 +79,7 @@ val forLoopsPhase = makeIrFilePhase( * } while (loopVar != last) * } * ``` - * If loop is an until loop (e.g., `for (i in A until B)`), it is transformed into: + * If loop is an until loop (e.g., `for (i in A until B)` or `for (i in A.. { + private val progressionElementTypes = context.ir.symbols.progressionElementTypes + + override fun matchIterable(expression: IrCall): Boolean { + val callee = expression.symbol.owner + return callee.valueParameters.singleOrNull()?.type in progressionElementTypes && + callee.extensionReceiverParameter == null && + callee.dispatchReceiverParameter?.type in progressionElementTypes && + callee.name.asString() == "rangeUntil" + } + + override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo = + with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { + ProgressionHeaderInfo( + data, + first = expression.dispatchReceiver!!, + last = expression.getValueArgument(0)!!, + step = irInt(1), + canOverflow = false, + isLastInclusive = false, + direction = ProgressionDirection.INCREASING + ) + } +} diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeUntil/forInRangeUntilChar.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeUntil/forInRangeUntilChar.kt new file mode 100644 index 00000000000..76e1ed3db43 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeUntil/forInRangeUntilChar.kt @@ -0,0 +1,37 @@ +// !OPT_IN: kotlin.ExperimentalStdlibApi +// IGNORE_BACKEND: JVM + +// IMPORTANT! +// Please, when your changes cause failures in bytecodeText tests for 'for' loops, +// examine the resulting bytecode shape carefully. +// Range and progression-based loops generated with Kotlin compiler should be +// as close as possible to Java counter loops ('for (int i = a; i < b; ++i) { ... }'). +// Otherwise it may result in performance regression due to missing HotSpot optimizations. +// Run Kotlin compiler benchmarks (https://github.com/Kotlin/kotlin-benchmarks) +// with compiler built from your changes if you are not sure. + +fun test(a: Char, b: Char): String { + var s = "" + for (i in a..