diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt index 33339985669..15c111c5c41 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt @@ -487,6 +487,7 @@ private val nameToOperationConventionOrigin = mutableMapOf( OperatorNameConventions.MOD to IrStatementOrigin.PERC, OperatorNameConventions.REM to IrStatementOrigin.PERC, OperatorNameConventions.RANGE_TO to IrStatementOrigin.RANGE, + OperatorNameConventions.RANGE_UNTIL to IrStatementOrigin.RANGE_UNTIL, OperatorNameConventions.CONTAINS to IrStatementOrigin.IN, ) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt index e99004c7e27..34d6140e6d5 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt @@ -126,6 +126,7 @@ class IrBuiltInsOverFir( createMemberFunction(OperatorNameConventions.MINUS, intType, "other" to charType, isOperator = true) val charRange = referenceClassByClassId(StandardClassIds.CharRange)!!.owner.defaultType createMemberFunction(OperatorNameConventions.RANGE_TO, charRange, "other" to charType, isIntrinsicConst = false) + createMemberFunction(OperatorNameConventions.RANGE_UNTIL, charRange, "other" to charType, isIntrinsicConst = false) createIntrinsicConstOfToStringAndEquals() finalizeClassDefinition() } diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index 1abf7f6248b..31df7b5fdc1 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -32671,6 +32671,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/operatorConventions/remOverModOperation.kt"); } + @Test + @TestMetadata("untilOperator.kt") + public void testUntilOperator() throws Exception { + runTest("compiler/testData/codegen/box/operatorConventions/untilOperator.kt"); + } + @Nested @TestMetadata("compiler/testData/codegen/box/operatorConventions/compareTo") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Range.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Range.kt new file mode 100644 index 00000000000..dcad462cd31 --- /dev/null +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Range.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2010-2016 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.backend.jvm.intrinsics + +import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo +import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression +import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature +import org.jetbrains.kotlin.types.AbstractTypeMapper +import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter +import java.lang.IllegalStateException + +abstract class Range(private val kind: RangeKind) : IntrinsicMethod() { + private fun mapRangeTypeToPrimitiveType(rangeType: Type, kind: RangeKind): Type { + val fqName = rangeType.internalName + return when (fqName.substringAfter("kotlin/ranges/").substringBefore("Range")) { + "Double" -> Type.DOUBLE_TYPE + "Float" -> Type.FLOAT_TYPE + "Long" -> Type.LONG_TYPE + "Int" -> Type.INT_TYPE + "Short" -> Type.SHORT_TYPE + "Char" -> Type.CHAR_TYPE + "Byte" -> Type.BYTE_TYPE + else -> throw IllegalStateException("${kind.desc} intrinsic can only work for primitive types: $fqName") + } + } + + override fun toCallable( + expression: IrFunctionAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext + ): IrIntrinsicFunction { + val argType = mapRangeTypeToPrimitiveType(signature.returnType, kind) + return object : IrIntrinsicFunction(expression, signature, context, listOf(argType) + signature.valueParameters.map { argType }) { + override fun genInvokeInstruction(v: InstructionAdapter) { + v.invokespecial( + signature.returnType.internalName, + "", + Type.getMethodDescriptor(Type.VOID_TYPE, argType, argType), + false + ) + } + + override fun invoke( + v: InstructionAdapter, + codegen: ExpressionCodegen, + data: BlockInfo, + expression: IrFunctionAccessExpression + ): StackValue { + codegen.markLineNumber(expression) + v.anew(returnType) + v.dup() + return super.invoke(v, codegen, data, expression) + } + } + } + + enum class RangeKind(val desc: String) { TO("RangeTo"), UNTIL("RangeUntil") } +} diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/RangeTo.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/RangeTo.kt index 097e988190c..607784184b9 100644 --- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/RangeTo.kt +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/RangeTo.kt @@ -16,51 +16,4 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics -import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo -import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen -import org.jetbrains.kotlin.backend.jvm.JvmBackendContext -import org.jetbrains.kotlin.codegen.StackValue -import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression -import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature -import org.jetbrains.org.objectweb.asm.Type -import org.jetbrains.org.objectweb.asm.Type.* -import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter -import java.lang.IllegalStateException - -object RangeTo : IntrinsicMethod() { - private fun rangeTypeToPrimitiveType(rangeType: Type): Type { - val fqName = rangeType.internalName - val name = fqName.substringAfter("kotlin/ranges/").substringBefore("Range") - return when (name) { - "Double" -> DOUBLE_TYPE - "Float" -> FLOAT_TYPE - "Long" -> LONG_TYPE - "Int" -> INT_TYPE - "Short" -> SHORT_TYPE - "Char" -> CHAR_TYPE - "Byte" -> BYTE_TYPE - else -> throw IllegalStateException("RangeTo intrinsic can only work for primitive types: $fqName") - } - } - - override fun toCallable(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction { - val argType = rangeTypeToPrimitiveType(signature.returnType) - return object: IrIntrinsicFunction(expression, signature, context, listOf(argType) + signature.valueParameters.map { argType }) { - override fun genInvokeInstruction(v: InstructionAdapter) { - v.invokespecial(signature.returnType.internalName, "", Type.getMethodDescriptor(Type.VOID_TYPE, argType, argType), false) - } - - override fun invoke( - v: InstructionAdapter, - codegen: ExpressionCodegen, - data: BlockInfo, - expression: IrFunctionAccessExpression - ): StackValue { - codegen.markLineNumber(expression) - v.anew(returnType) - v.dup() - return super.invoke(v, codegen, data, expression) - } - } - } -} +object RangeTo : Range(RangeKind.TO) diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/RangeUntil.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/RangeUntil.kt new file mode 100644 index 00000000000..4ccc2c4e8b0 --- /dev/null +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/RangeUntil.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2010-2016 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.backend.jvm.intrinsics + +object RangeUntil : Range(RangeKind.UNTIL) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorConventions.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorConventions.kt index 1c1eb4be7ff..fb64ef5e2d6 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorConventions.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorConventions.kt @@ -36,6 +36,7 @@ fun getInfixOperator(ktOperator: IElementType): IrStatementOrigin? = KtTokens.DIV -> IrStatementOrigin.DIV KtTokens.PERC -> IrStatementOrigin.PERC KtTokens.RANGE -> IrStatementOrigin.RANGE + KtTokens.RANGE_UNTIL -> IrStatementOrigin.RANGE_UNTIL KtTokens.LT -> IrStatementOrigin.LT KtTokens.LTEQ -> IrStatementOrigin.LTEQ KtTokens.GT -> IrStatementOrigin.GT @@ -90,6 +91,7 @@ val OPERATORS_DESUGARED_TO_CALLS = IrStatementOrigin.DIV, IrStatementOrigin.PERC, IrStatementOrigin.RANGE, + IrStatementOrigin.RANGE_UNTIL, IrStatementOrigin.EXCL, IrStatementOrigin.UMINUS, IrStatementOrigin.UPLUS diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt index c4c82be83d6..d69ec7fc4d9 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt @@ -40,6 +40,7 @@ interface IrStatementOrigin { object DIV : IrStatementOriginImpl("DIV") object PERC : IrStatementOriginImpl("PERC") object RANGE : IrStatementOriginImpl("RANGE") + object RANGE_UNTIL : IrStatementOriginImpl("RANGE_UNTIL") object INVOKE : IrStatementOriginImpl("INVOKE") object VARIABLE_AS_FUNCTION : IrStatementOriginImpl("VARIABLE_AS_FUNCTION") diff --git a/compiler/testData/codegen/box/operatorConventions/untilOperator.kt b/compiler/testData/codegen/box/operatorConventions/untilOperator.kt new file mode 100644 index 00000000000..24f9f89c000 --- /dev/null +++ b/compiler/testData/codegen/box/operatorConventions/untilOperator.kt @@ -0,0 +1,47 @@ +// WITH_STDLIB +// !LANGUAGE: +RangeUntilOperator + +class ARange(_start: A, _end: A): ClosedRange, Iterable { + override val endInclusive: A = _end + override val start: A = _start + override fun iterator(): Iterator = object : Iterator { + override fun hasNext(): Boolean = hasNext + override fun next(): A { + val value = next + if (value == finalElement) { + if (!hasNext) throw kotlin.NoSuchElementException() + hasNext = false + } + else { + next += step + } + return A(value) + } + } + + private val step: Int = 1 + private val finalElement: Int = endInclusive.x + private var hasNext: Boolean = if (step > 0) start <= endInclusive else start >= endInclusive + private var next: Int = if (hasNext) start.x else finalElement +} + +class A(val x: Int): Comparable { + operator fun rangeUntil(other: A): Iterable = ARange(this, A(other.x - 1)) + operator fun rangeTo(other: A): Iterable = ARange(this, other) + override fun compareTo(other: A): Int = this.x.compareTo(other.x) +} + +fun box(): String { + val x = A(1) + val y = A(4) + + var summ1 = 0 + for (i in x..