From e9b0ebb95e1ab4b2457ae5c28a2e46096d01a7ce Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Wed, 30 Nov 2016 15:38:47 +0300 Subject: [PATCH] Ranges support. (#73) --- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 10 +- backend.native/tests/build.gradle | 5 + .../tests/runtime/collections/range0.kt | 6 + runtime/src/main/kotlin/kotlin/Char.kt | 4 +- runtime/src/main/kotlin/kotlin/Primitives.kt | 86 ++++++--- .../kotlin/kotlin/internal/progressionUtil.kt | 71 ++++++++ .../kotlin/ranges/ProgressionIterators.kt | 70 ++++++++ .../main/kotlin/kotlin/ranges/Progressions.kt | 165 ++++++++++++++++++ .../src/main/kotlin/kotlin/ranges/Range.kt | 27 +++ .../src/main/kotlin/kotlin/ranges/Ranges.kt | 79 +++++++++ 10 files changed, 493 insertions(+), 30 deletions(-) create mode 100644 backend.native/tests/runtime/collections/range0.kt create mode 100644 runtime/src/main/kotlin/kotlin/internal/progressionUtil.kt create mode 100644 runtime/src/main/kotlin/kotlin/ranges/ProgressionIterators.kt create mode 100644 runtime/src/main/kotlin/kotlin/ranges/Progressions.kt create mode 100644 runtime/src/main/kotlin/kotlin/ranges/Range.kt create mode 100644 runtime/src/main/kotlin/kotlin/ranges/Ranges.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index cc28319a9f8..12c82392709 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -224,6 +224,11 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// override fun visitConstructor(constructorDeclaration: IrConstructor) { + if (constructorDeclaration.descriptor.containingDeclaration.isIntrinsic) { + // Do not generate any ctors for intrinsic classes. + return + } + codegen.prologue(constructorDeclaration) val constructorDescriptor = constructorDeclaration.descriptor val classDescriptor = DescriptorUtils.getContainingClass(constructorDescriptor) @@ -389,8 +394,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid return } - if (declaration.descriptor.isIntrinsic) { - // do not generate any code for intrinsic classes as they require special handling + if (KotlinBuiltIns.isUnit(declaration.descriptor.defaultType)) { + // Do not generate any code for Unit. + // TODO: Unit has toString() operation, which we may want to support. return } diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index ecec21959fd..e5ad093797a 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -574,3 +574,8 @@ task unreachable1(type: RunKonanTest) { goldValue = "1\n" source = "codegen/controlflow/unreachable1.kt" } + +task range0(type: RunKonanTest) { + goldValue = "123\nabcd\n" + source = "runtime/collections/range0.kt" +} diff --git a/backend.native/tests/runtime/collections/range0.kt b/backend.native/tests/runtime/collections/range0.kt new file mode 100644 index 00000000000..5e80f26dd15 --- /dev/null +++ b/backend.native/tests/runtime/collections/range0.kt @@ -0,0 +1,6 @@ +fun main(args : Array) { + for (i in 1..3) print(i) + println() + for (i in 'a'..'d') print(i) + println() +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/Char.kt b/runtime/src/main/kotlin/kotlin/Char.kt index 9f036807212..006b37fe727 100644 --- a/runtime/src/main/kotlin/kotlin/Char.kt +++ b/runtime/src/main/kotlin/kotlin/Char.kt @@ -33,7 +33,9 @@ public final class Char : Comparable { external public operator fun dec(): Char /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Char): CharRange + public operator fun rangeTo(other: Char): CharRange { + return CharRange(this, other) + } /** Returns the value of this character as a `Byte`. */ @SymbolName("Kotlin_Char_toByte") diff --git a/runtime/src/main/kotlin/kotlin/Primitives.kt b/runtime/src/main/kotlin/kotlin/Primitives.kt index 9bbad80bff1..0593c9b580a 100644 --- a/runtime/src/main/kotlin/kotlin/Primitives.kt +++ b/runtime/src/main/kotlin/kotlin/Primitives.kt @@ -168,15 +168,6 @@ public final class Byte : Number(), Comparable { @SymbolName("Kotlin_Byte_unaryMinus") external public operator fun unaryMinus(): Int - /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Byte): IntRange - /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Short): IntRange - /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Int): IntRange - /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Long): LongRange - @SymbolName("Kotlin_Byte_toByte") external public override fun toByte(): Byte @SymbolName("Kotlin_Byte_toChar") @@ -192,6 +183,23 @@ public final class Byte : Number(), Comparable { @SymbolName("Kotlin_Byte_toDouble") external public override fun toDouble(): Double + /** Creates a range from this value to the specified [other] value. */ + public operator fun rangeTo(other: Byte): IntRange { + return IntRange(this.toInt(), other.toInt()) + } + /** Creates a range from this value to the specified [other] value. */ + public operator fun rangeTo(other: Short): IntRange { + return IntRange(this.toInt(), other.toInt()) + } + /** Creates a range from this value to the specified [other] value. */ + public operator fun rangeTo(other: Int): IntRange { + return IntRange(this.toInt(), other.toInt()) + } + /** Creates a range from this value to the specified [other] value. */ + public operator fun rangeTo(other: Long): LongRange { + return LongRange(this.toLong(), other.toLong()) + } + // Konan-specific. @SymbolName("Kotlin_Byte_toString") external public override fun toString(): String @@ -206,7 +214,7 @@ public final class Short : Number(), Comparable { /** * A constant holding the minimum value an instance of Short can have. */ - // public const val MIN_VALUE: Short = -32768 + public const val MIN_VALUE: Short = -32768 /** * A constant holding the maximum value an instance of Short can have. @@ -366,13 +374,21 @@ public final class Short : Number(), Comparable { external public operator fun unaryMinus(): Int /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Byte): IntRange + public operator fun rangeTo(other: Byte): IntRange { + return IntRange(this.toInt(), other.toInt()) + } /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Short): IntRange + public operator fun rangeTo(other: Short): IntRange { + return IntRange(this.toInt(), other.toInt()) + } /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Int): IntRange + public operator fun rangeTo(other: Int): IntRange { + return IntRange(this.toInt(), other.toInt()) + } /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Long): LongRange + public operator fun rangeTo(other: Long): LongRange { + return LongRange(this.toLong(), other.toLong()) + } @SymbolName("Kotlin_Short_toByte") external public override fun toByte(): Byte @@ -562,15 +578,6 @@ public final class Int : Number(), Comparable { @SymbolName("Kotlin_Int_unaryMinus") external public operator fun unaryMinus(): Int - /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Byte): IntRange - /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Short): IntRange - /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Int): IntRange - /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Long): LongRange - /** Shifts this value left by [bits]. */ @SymbolName("Kotlin_Int_shl_Int") external public infix fun shl(bitCount: Int): Int @@ -593,6 +600,23 @@ public final class Int : Number(), Comparable { @SymbolName("Kotlin_Int_inv") external public fun inv(): Int + /** Creates a range from this value to the specified [other] value. */ + public operator fun rangeTo(other: Byte): IntRange { + return IntRange(this, other.toInt()) + } + /** Creates a range from this value to the specified [other] value. */ + public operator fun rangeTo(other: Short): IntRange { + return IntRange(this, other.toInt()) + } + /** Creates a range from this value to the specified [other] value. */ + public operator fun rangeTo(other: Int): IntRange { + return IntRange(this, other.toInt()) + } + /** Creates a range from this value to the specified [other] value. */ + public operator fun rangeTo(other: Long): LongRange { + return LongRange(this.toLong(), other.toLong()) + } + @SymbolName("Kotlin_Int_toByte") external public override fun toByte(): Byte @SymbolName("Kotlin_Int_toChar") @@ -782,13 +806,21 @@ public final class Long : Number(), Comparable { external public operator fun unaryMinus(): Long /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Byte): LongRange + external public operator fun rangeTo(other: Byte): LongRange { + return LongRange(this, other.toLong()) + } /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Short): LongRange + public operator fun rangeTo(other: Short): LongRange { + return LongRange(this, other.toLong()) + } /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Int): LongRange + public operator fun rangeTo(other: Int): LongRange { + return LongRange(this, other.toLong()) + } /** Creates a range from this value to the specified [other] value. */ - // external public operator fun rangeTo(other: Long): LongRange + public operator fun rangeTo(other: Long): LongRange { + return LongRange(this, other.toLong()) + } /** Shifts this value left by [bits]. */ @SymbolName("Kotlin_Long_shl_Long") diff --git a/runtime/src/main/kotlin/kotlin/internal/progressionUtil.kt b/runtime/src/main/kotlin/kotlin/internal/progressionUtil.kt new file mode 100644 index 00000000000..d92be24c5c0 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/internal/progressionUtil.kt @@ -0,0 +1,71 @@ +package kotlin.internal + +// a mod b (in arithmetical sense) +private fun mod(a: Int, b: Int): Int { + val mod = a % b + return if (mod >= 0) mod else mod + b +} + +private fun mod(a: Long, b: Long): Long { + val mod = a % b + return if (mod >= 0) mod else mod + b +} + +// (a - b) mod c +private fun differenceModulo(a: Int, b: Int, c: Int): Int { + return mod(mod(a, c) - mod(b, c), c) +} + +private fun differenceModulo(a: Long, b: Long, c: Long): Long { + return mod(mod(a, c) - mod(b, c), c) +} + +/** + * Calculates the final element of a bounded arithmetic progression, i.e. the last element of the progression which is in the range + * from [start] to [end] in case of a positive [step], or from [end] to [start] in case of a negative + * [step]. + * + * No validation on passed parameters is performed. The given parameters should satisfy the condition: either + * `step > 0` and `start >= end`, or `step < 0` and`start >= end`. + * @param start first element of the progression + * @param end ending bound for the progression + * @param step increment, or difference of successive elements in the progression + * @return the final element of the progression + * @suppress + */ +internal fun getProgressionLastElement(start: Int, end: Int, step: Int): Int { + if (step > 0) { + return end - differenceModulo(end, start, step) + } + else if (step < 0) { + return end + differenceModulo(start, end, -step) + } + else { + throw kotlin.IllegalArgumentException("Step is zero.") + } +} + +/** + * Calculates the final element of a bounded arithmetic progression, i.e. the last element of the progression which is in the range + * from [start] to [end] in case of a positive [step], or from [end] to [start] in case of a negative + * [step]. + * + * No validation on passed parameters is performed. The given parameters should satisfy the condition: either + * `step > 0` and `start >= end`, or `step < 0` and`start >= end`. + * @param start first element of the progression + * @param end ending bound for the progression + * @param step increment, or difference of successive elements in the progression + * @return the final element of the progression + * @suppress + */ +internal fun getProgressionLastElement(start: Long, end: Long, step: Long): Long { + if (step > 0) { + return end - differenceModulo(end, start, step) + } + else if (step < 0) { + return end + differenceModulo(start, end, -step) + } + else { + throw kotlin.IllegalArgumentException("Step is zero.") + } +} diff --git a/runtime/src/main/kotlin/kotlin/ranges/ProgressionIterators.kt b/runtime/src/main/kotlin/kotlin/ranges/ProgressionIterators.kt new file mode 100644 index 00000000000..96c55429576 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/ranges/ProgressionIterators.kt @@ -0,0 +1,70 @@ +package kotlin.ranges + +/** + * An iterator over a progression of values of type `Char`. + * @property step the number by which the value is incremented on each step. + */ +internal class CharProgressionIterator(first: Char, last: Char, val step: Int) : CharIterator() { + private var next = first.toInt() + private val finalElement = last.toInt() + private var hasNext: Boolean = if (step > 0) first <= last else first >= last + + override fun hasNext(): Boolean = hasNext + + override fun nextChar(): Char { + val value = next + if (value == finalElement) { + hasNext = false + } + else { + next += step + } + return value.toChar() + } +} + +/** + * An iterator over a progression of values of type `Int`. + * @property step the number by which the value is incremented on each step. + */ +internal class IntProgressionIterator(first: Int, last: Int, val step: Int) : IntIterator() { + private var next = first + private val finalElement = last + private var hasNext: Boolean = if (step > 0) first <= last else first >= last + + override fun hasNext(): Boolean = hasNext + + override fun nextInt(): Int { + val value = next + if (value == finalElement) { + hasNext = false + } + else { + next += step + } + return value + } +} + +/** + * An iterator over a progression of values of type `Long`. + * @property step the number by which the value is incremented on each step. + */ +internal class LongProgressionIterator(first: Long, last: Long, val step: Long) : LongIterator() { + private var next = first + private val finalElement = last + private var hasNext: Boolean = if (step > 0) first <= last else first >= last + + override fun hasNext(): Boolean = hasNext + + override fun nextLong(): Long { + val value = next + if (value == finalElement) { + hasNext = false + } + else { + next += step + } + return value + } +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/ranges/Progressions.kt b/runtime/src/main/kotlin/kotlin/ranges/Progressions.kt new file mode 100644 index 00000000000..97c4085618a --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/ranges/Progressions.kt @@ -0,0 +1,165 @@ +package kotlin.ranges + +import kotlin.internal.getProgressionLastElement + +/** + * A progression of values of type `Char`. + */ +public open class CharProgression +internal constructor +( + start: Char, + endInclusive: Char, + step: Int +) : Iterable { + init { + if (step == 0) throw kotlin.IllegalArgumentException("Step must be non-zero") + } + + /** + * The first element in the progression. + */ + public val first: Char = start + + /** + * The last element in the progression. + */ + public val last: Char = getProgressionLastElement(start.toInt(), endInclusive.toInt(), step).toChar() + + /** + * The step of the progression. + */ + public val step: Int = step + + override fun iterator(): CharIterator = CharProgressionIterator(first, last, step) + + /** Checks if the progression is empty. */ + public open fun isEmpty(): Boolean = if (step > 0) first > last else first < last + + override fun equals(other: Any?): Boolean = + other is CharProgression && (isEmpty() && other.isEmpty() || + first == other.first && last == other.last && step == other.step) + + override fun hashCode(): Int = + if (isEmpty()) -1 else (31 * (31 * first.toInt() + last.toInt()) + step) + + override fun toString(): String = if (step > 0) "$first..$last step $step" else "$first downTo $last step ${-step}" + + companion object { + /** + * Creates CharProgression within the specified bounds of a closed range. + + * The progression starts with the [rangeStart] value and goes toward the [rangeEnd] value not excluding it, with the specified [step]. + * In order to go backwards the [step] must be negative. + */ + public fun fromClosedRange(rangeStart: Char, rangeEnd: Char, step: Int): CharProgression = CharProgression(rangeStart, rangeEnd, step) + } +} + +/** + * A progression of values of type `Int`. + */ +public open class IntProgression +internal constructor +( + start: Int, + endInclusive: Int, + step: Int +) : Iterable { + init { + if (step == 0) throw kotlin.IllegalArgumentException("Step must be non-zero") + } + + /** + * The first element in the progression. + */ + public val first: Int = start + + /** + * The last element in the progression. + */ + public val last: Int = getProgressionLastElement(start.toInt(), endInclusive.toInt(), step).toInt() + + /** + * The step of the progression. + */ + public val step: Int = step + + override fun iterator(): IntIterator = IntProgressionIterator(first, last, step) + + /** Checks if the progression is empty. */ + public open fun isEmpty(): Boolean = if (step > 0) first > last else first < last + + override fun equals(other: Any?): Boolean = + other is IntProgression && (isEmpty() && other.isEmpty() || + first == other.first && last == other.last && step == other.step) + + override fun hashCode(): Int = + if (isEmpty()) -1 else (31 * (31 * first + last) + step) + + override fun toString(): String = if (step > 0) "$first..$last step $step" else "$first downTo $last step ${-step}" + + companion object { + /** + * Creates IntProgression within the specified bounds of a closed range. + + * The progression starts with the [rangeStart] value and goes toward the [rangeEnd] value not excluding it, with the specified [step]. + * In order to go backwards the [step] must be negative. + */ + public fun fromClosedRange(rangeStart: Int, rangeEnd: Int, step: Int): IntProgression = IntProgression(rangeStart, rangeEnd, step) + } +} + +/** + * A progression of values of type `Long`. + */ +public open class LongProgression +internal constructor +( + start: Long, + endInclusive: Long, + step: Long +) : Iterable { + init { + if (step == 0L) throw kotlin.IllegalArgumentException("Step must be non-zero") + } + + /** + * The first element in the progression. + */ + public val first: Long = start + + /** + * The last element in the progression. + */ + public val last: Long = getProgressionLastElement(start.toLong(), endInclusive.toLong(), step).toLong() + + /** + * The step of the progression. + */ + public val step: Long = step + + override fun iterator(): LongIterator = LongProgressionIterator(first, last, step) + + /** Checks if the progression is empty. */ + public open fun isEmpty(): Boolean = if (step > 0) first > last else first < last + + override fun equals(other: Any?): Boolean = + other is LongProgression && (isEmpty() && other.isEmpty() || + first == other.first && last == other.last && step == other.step) + + override fun hashCode(): Int = + if (isEmpty()) -1 else (31 * (31 * (first xor (first ushr 32)) + (last xor (last ushr 32))) + (step xor (step ushr 32))).toInt() + + override fun toString(): String = if (step > 0) "$first..$last step $step" else "$first downTo $last step ${-step}" + + companion object { + /** + * Creates LongProgression within the specified bounds of a closed range. + + * The progression starts with the [rangeStart] value and goes toward the [rangeEnd] value not excluding it, with the specified [step]. + * In order to go backwards the [step] must be negative. + */ + public fun fromClosedRange(rangeStart: Long, rangeEnd: Long, step: Long): LongProgression = LongProgression(rangeStart, rangeEnd, step) + } +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/ranges/Range.kt b/runtime/src/main/kotlin/kotlin/ranges/Range.kt new file mode 100644 index 00000000000..9bd141fd53b --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/ranges/Range.kt @@ -0,0 +1,27 @@ +package kotlin.ranges + +/** + * Represents a range of values (for example, numbers or characters). + * See the [Kotlin language documentation](http://kotlinlang.org/docs/reference/ranges.html) for more information. + */ +public interface ClosedRange> { + /** + * The minimum value in the range. + */ + public val start: T + + /** + * The maximum value in the range (inclusive). + */ + public val endInclusive: T + + /** + * Checks whether the specified [value] belongs to the range. + */ + public operator fun contains(value: T): Boolean = value >= start && value <= endInclusive + + /** + * Checks whether the range is empty. + */ + public fun isEmpty(): Boolean = start > endInclusive +} diff --git a/runtime/src/main/kotlin/kotlin/ranges/Ranges.kt b/runtime/src/main/kotlin/kotlin/ranges/Ranges.kt new file mode 100644 index 00000000000..7e1357facd0 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/ranges/Ranges.kt @@ -0,0 +1,79 @@ +package kotlin.ranges + +/** + * A range of values of type `Char`. + */ +public class CharRange(start: Char, endInclusive: Char) : CharProgression(start, endInclusive, 1), ClosedRange { + override val start: Char get() = first + override val endInclusive: Char get() = last + + override fun contains(value: Char): Boolean = first <= value && value <= last + + override fun isEmpty(): Boolean = first > last + + override fun equals(other: Any?): Boolean = + other is CharRange && (isEmpty() && other.isEmpty() || + first == other.first && last == other.last) + + override fun hashCode(): Int = + if (isEmpty()) -1 else (31 * first.toInt() + last.toInt()) + + override fun toString(): String = "$first..$last" + + companion object { + /** An empty range of values of type Char. */ + public val EMPTY: CharRange = CharRange(1.toChar(), 0.toChar()) + } +} + +/** + * A range of values of type `Int`. + */ +public class IntRange(start: Int, endInclusive: Int) : IntProgression(start, endInclusive, 1), ClosedRange { + override val start: Int get() = first + override val endInclusive: Int get() = last + + override fun contains(value: Int): Boolean = first <= value && value <= last + + override fun isEmpty(): Boolean = first > last + + override fun equals(other: Any?): Boolean = + other is IntRange && (isEmpty() && other.isEmpty() || + first == other.first && last == other.last) + + override fun hashCode(): Int = + if (isEmpty()) -1 else (31 * first + last) + + override fun toString(): String = "$first..$last" + + companion object { + /** An empty range of values of type Int. */ + public val EMPTY: IntRange = IntRange(1, 0) + } +} + +/** + * A range of values of type `Long`. + */ +public class LongRange(start: Long, endInclusive: Long) : LongProgression(start, endInclusive, 1), ClosedRange { + override val start: Long get() = first + override val endInclusive: Long get() = last + + override fun contains(value: Long): Boolean = first <= value && value <= last + + override fun isEmpty(): Boolean = first > last + + override fun equals(other: Any?): Boolean = + other is LongRange && (isEmpty() && other.isEmpty() || + first == other.first && last == other.last) + + override fun hashCode(): Int = + if (isEmpty()) -1 else (31 * (first xor (first ushr 32)) + (last xor (last ushr 32))).toInt() + + override fun toString(): String = "$first..$last" + + companion object { + /** An empty range of values of type Long. */ + public val EMPTY: LongRange = LongRange(1, 0) + } +} \ No newline at end of file