diff --git a/libraries/stdlib/src/generated/_Ranges.kt b/libraries/stdlib/src/generated/_Ranges.kt index e9c1731290b..6141f8ffaa4 100644 --- a/libraries/stdlib/src/generated/_Ranges.kt +++ b/libraries/stdlib/src/generated/_Ranges.kt @@ -866,30 +866,65 @@ public fun Double.coerceIn(minimumValue: Double, maximumValue: Double): Double { /** * Ensures that this value lies in the specified [range]. * - * @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + * @return this value if it's in the [range], or `range.start` if this value is less than `range.start`, or `range.endInclusive` if this value is greater than `range.endInclusive`. + */ +public fun > T.coerceIn(range: ClosedComparableRange): T { + if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: $range.") + return when { + !range.lessThanOrEquals(range.start, this) -> range.start + !range.lessThanOrEquals(this, range.endInclusive) -> range.endInclusive + else -> this + } +} + +/** + * Ensures that this value lies in the specified [range]. + * + * @return this value if it's in the [range], or `range.start` if this value is less than `range.start`, or `range.endInclusive` if this value is greater than `range.endInclusive`. */ public fun > T.coerceIn(range: ClosedRange): T { + if (range is ClosedComparableRange) { + return this.coerceIn(range) + } if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: $range.") - return if (this < range.start) range.start else if (this > range.endInclusive) range.endInclusive else this + return when { + this < range.start -> range.start + this > range.endInclusive -> range.endInclusive + else -> this + } } /** * Ensures that this value lies in the specified [range]. * - * @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + * @return this value if it's in the [range], or `range.start` if this value is less than `range.start`, or `range.endInclusive` if this value is greater than `range.endInclusive`. */ public fun Int.coerceIn(range: ClosedRange): Int { + if (range is ClosedComparableRange) { + return this.coerceIn(range) + } if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: $range.") - return if (this < range.start) range.start else if (this > range.endInclusive) range.endInclusive else this + return when { + this < range.start -> range.start + this > range.endInclusive -> range.endInclusive + else -> this + } } /** * Ensures that this value lies in the specified [range]. * - * @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + * @return this value if it's in the [range], or `range.start` if this value is less than `range.start`, or `range.endInclusive` if this value is greater than `range.endInclusive`. */ public fun Long.coerceIn(range: ClosedRange): Long { + if (range is ClosedComparableRange) { + return this.coerceIn(range) + } if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: $range.") - return if (this < range.start) range.start else if (this > range.endInclusive) range.endInclusive else this + return when { + this < range.start -> range.start + this > range.endInclusive -> range.endInclusive + else -> this + } } diff --git a/libraries/stdlib/test/ranges/CoercionTest.kt b/libraries/stdlib/test/ranges/CoercionTest.kt index 69538a11d49..e63026cfe82 100644 --- a/libraries/stdlib/test/ranges/CoercionTest.kt +++ b/libraries/stdlib/test/ranges/CoercionTest.kt @@ -84,8 +84,8 @@ class CoercionTest { assertFails { 1.0.coerceIn(1.0, 0.0) } assertFails { 1.0.coerceIn(1.0..0.0) } - assertTrue(0.0 == 0.0.coerceIn(0.0, -0.0)) - assertTrue(0.0 == 0.0.coerceIn(0.0..-0.0)) + assertTrue(0.0.equals(0.0.coerceIn(0.0, -0.0))) + assertTrue((-0.0).equals((-0.0).coerceIn(0.0..-0.0))) } @Test diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt index 9020828ac25..db7717c59b0 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt @@ -1904,6 +1904,7 @@ public final class kotlin/ranges/RangesKt { public static final fun coerceIn (JJJ)J public static final fun coerceIn (JLkotlin/ranges/ClosedRange;)J public static final fun coerceIn (Ljava/lang/Comparable;Ljava/lang/Comparable;Ljava/lang/Comparable;)Ljava/lang/Comparable; + public static final fun coerceIn (Ljava/lang/Comparable;Lkotlin/ranges/ClosedComparableRange;)Ljava/lang/Comparable; public static final fun coerceIn (Ljava/lang/Comparable;Lkotlin/ranges/ClosedRange;)Ljava/lang/Comparable; public static final fun coerceIn (SSS)S public static final fun doubleRangeContains (Lkotlin/ranges/ClosedRange;B)Z diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Comparables.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Comparables.kt index 5b123665577..a7a9beb92c2 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Comparables.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Comparables.kt @@ -56,13 +56,57 @@ fun comparables(): List { """ Ensures that this value lies in the specified [range]. - @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + @return this value if it's in the [range], or `range.start` if this value is less than `range.start`, or `range.endInclusive` if this value is greater than `range.endInclusive`. """ } - body { + body(Generic) { + """ + if (range is ClosedComparableRange) { + return this.coerceIn(range) + } + if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: ${'$'}range.") + return when { + this < range.start -> range.start + this > range.endInclusive -> range.endInclusive + else -> this + } + """ + } + body(Primitives) { + """ + if (range is ClosedComparableRange) { + return this.coerceIn(range) + } + if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: ${'$'}range.") + return when { + this < range.start -> range.start + this > range.endInclusive -> range.endInclusive + else -> this + } + """ + } + } + + templates add f("coerceIn(range: ClosedComparableRange)") { + sourceFile(SourceFile.Ranges) + only(Generic) + returns("SELF") + typeParam("T: Comparable") + doc { + """ + Ensures that this value lies in the specified [range]. + + @return this value if it's in the [range], or `range.start` if this value is less than `range.start`, or `range.endInclusive` if this value is greater than `range.endInclusive`. + """ + } + body(Generic) { """ if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: ${'$'}range.") - return if (this < range.start) range.start else if (this > range.endInclusive) range.endInclusive else this + return when { + !range.lessThanOrEquals(range.start, this) -> range.start + !range.lessThanOrEquals(this, range.endInclusive) -> range.endInclusive + else -> this + } """ } }