Floor division and remainder for numeric types KT-26234
- floorDiv/mod for unsigned types - floorDiv/mod for signed types - mod for floating point types - mod return type: same as divisor for integer types - Update JS API dump - Avoid triggering division overflow in tests due to K/N - Workaround failing test in JS-legacy
This commit is contained in:
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.generators.builtins
|
||||
@@ -73,4 +62,6 @@ fun areEqualNumbers(v: String) = "$v == other.$v"
|
||||
|
||||
fun hashLong(v: String) = "($v xor ($v ushr 32))"
|
||||
|
||||
fun convert(v: String, from: UnsignedType, to: UnsignedType) = if (from == to) v else "$v.to${to.capitalized}()"
|
||||
fun convert(v: String, from: UnsignedType, to: UnsignedType) = if (from == to) v else "$v.to${to.capitalized}()"
|
||||
|
||||
fun convert(v: String, from: PrimitiveType, to: PrimitiveType) = if (from == to) v else "$v.to${to.capitalized}()"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@@ -11,7 +11,8 @@ import org.jetbrains.kotlin.generators.builtins.functions.GenerateFunctions
|
||||
import org.jetbrains.kotlin.generators.builtins.iterators.GenerateIterators
|
||||
import org.jetbrains.kotlin.generators.builtins.progressionIterators.GenerateProgressionIterators
|
||||
import org.jetbrains.kotlin.generators.builtins.progressions.GenerateProgressions
|
||||
import org.jetbrains.kotlin.generators.builtins.ranges.GeneratePrimitives
|
||||
import org.jetbrains.kotlin.generators.builtins.numbers.GeneratePrimitives
|
||||
import org.jetbrains.kotlin.generators.builtins.numbers.GenerateFloorDivMod
|
||||
import org.jetbrains.kotlin.generators.builtins.ranges.GenerateRanges
|
||||
import org.jetbrains.kotlin.generators.builtins.unsigned.generateUnsignedTypes
|
||||
import org.xml.sax.InputSource
|
||||
@@ -27,16 +28,14 @@ val BUILT_INS_NATIVE_DIR = File("core/builtins/native/")
|
||||
val BUILT_INS_SRC_DIR = File("core/builtins/src/")
|
||||
val RUNTIME_JVM_DIR = File("libraries/stdlib/jvm/runtime/")
|
||||
val UNSIGNED_TYPES_DIR = File("libraries/stdlib/unsigned/src")
|
||||
val STDLIB_DIR = File("libraries/stdlib/src")
|
||||
|
||||
abstract class BuiltInsSourceGenerator(val out: PrintWriter) {
|
||||
protected abstract fun generateBody(): Unit
|
||||
|
||||
protected open fun getPackage(): String = "kotlin"
|
||||
|
||||
enum class Language {
|
||||
KOTLIN,
|
||||
JAVA
|
||||
}
|
||||
protected open fun getMultifileClassName(): String? = null
|
||||
|
||||
fun generate() {
|
||||
out.println(readCopyrightNoticeFromProfile(File(".idea/copyright/apache.xml")))
|
||||
@@ -44,6 +43,10 @@ abstract class BuiltInsSourceGenerator(val out: PrintWriter) {
|
||||
// and we don't want to scare users with any internal information about our project
|
||||
out.println("// Auto-generated file. DO NOT EDIT!")
|
||||
out.println()
|
||||
getMultifileClassName()?.let { name ->
|
||||
out.println("@file:kotlin.jvm.JvmName(\"$name\")")
|
||||
out.println("@file:kotlin.jvm.JvmMultifileClass")
|
||||
}
|
||||
out.print("package ${getPackage()}")
|
||||
out.println()
|
||||
out.println()
|
||||
@@ -77,6 +80,7 @@ fun generateBuiltIns(generate: (File, (PrintWriter) -> BuiltInsSourceGenerator)
|
||||
generate(File(BUILT_INS_SRC_DIR, "kotlin/ProgressionIterators.kt")) { GenerateProgressionIterators(it) }
|
||||
generate(File(BUILT_INS_SRC_DIR, "kotlin/Progressions.kt")) { GenerateProgressions(it) }
|
||||
generate(File(BUILT_INS_SRC_DIR, "kotlin/Ranges.kt")) { GenerateRanges(it) }
|
||||
generate(File(STDLIB_DIR, "kotlin/util/FloorDivMod.kt")) { GenerateFloorDivMod(it) }
|
||||
|
||||
generateUnsignedTypes(UNSIGNED_TYPES_DIR, generate)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.generators.builtins.ranges
|
||||
package org.jetbrains.kotlin.generators.builtins.numbers
|
||||
|
||||
import org.jetbrains.kotlin.generators.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.generators.builtins.convert
|
||||
import org.jetbrains.kotlin.generators.builtins.generateBuiltIns.BuiltInsSourceGenerator
|
||||
import java.io.PrintWriter
|
||||
|
||||
@@ -396,13 +397,112 @@ class GeneratePrimitives(out: PrintWriter) : BuiltInsSourceGenerator(out) {
|
||||
out.println(" public override fun to$otherName(): $otherName")
|
||||
}
|
||||
}
|
||||
|
||||
private fun maxByDomainCapacity(type1: PrimitiveType, type2: PrimitiveType): PrimitiveType
|
||||
= if (type1.ordinal > type2.ordinal) type1 else type2
|
||||
|
||||
private fun getOperatorReturnType(kind1: PrimitiveType, kind2: PrimitiveType): PrimitiveType {
|
||||
require(kind1 != PrimitiveType.BOOLEAN) { "kind1 must not be BOOLEAN" }
|
||||
require(kind2 != PrimitiveType.BOOLEAN) { "kind2 must not be BOOLEAN" }
|
||||
return maxByDomainCapacity(maxByDomainCapacity(kind1, kind2), PrimitiveType.INT)
|
||||
}
|
||||
}
|
||||
|
||||
class GenerateFloorDivMod(out: PrintWriter) : BuiltInsSourceGenerator(out) {
|
||||
|
||||
override fun getMultifileClassName() = "NumbersKt"
|
||||
override fun generateBody() {
|
||||
out.println("import kotlin.math.sign")
|
||||
out.println()
|
||||
|
||||
val integerTypes = PrimitiveType.integral intersect PrimitiveType.onlyNumeric
|
||||
for (thisType in integerTypes) {
|
||||
for (otherType in integerTypes) {
|
||||
generateFloorDiv(thisType, otherType)
|
||||
generateMod(thisType, otherType)
|
||||
}
|
||||
}
|
||||
|
||||
val fpTypes = PrimitiveType.floatingPoint
|
||||
for (thisType in fpTypes) {
|
||||
for (otherType in fpTypes) {
|
||||
generateFpMod(thisType, otherType)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private fun generateFloorDiv(thisKind: PrimitiveType, otherKind: PrimitiveType) {
|
||||
val returnType = getOperatorReturnType(thisKind, otherKind)
|
||||
val returnTypeName = returnType.capitalized
|
||||
out.println("""@SinceKotlin("1.5")""")
|
||||
out.println("@kotlin.internal.InlineOnly")
|
||||
val declaration = "public inline fun ${thisKind.capitalized}.floorDiv(other: ${otherKind.capitalized}): $returnTypeName"
|
||||
if (thisKind == otherKind && thisKind >= PrimitiveType.INT) {
|
||||
out.println(
|
||||
"""
|
||||
$declaration {
|
||||
var q = this / other
|
||||
if (this xor other < 0 && q * other != this) q--
|
||||
return q
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
} else {
|
||||
out.println("$declaration = ")
|
||||
out.println(" ${
|
||||
convert("this", thisKind, returnType)}.floorDiv(${convert("other", otherKind, returnType)})")
|
||||
}
|
||||
out.println()
|
||||
}
|
||||
|
||||
private fun generateMod(thisKind: PrimitiveType, otherKind: PrimitiveType) {
|
||||
val operationType = getOperatorReturnType(thisKind, otherKind)
|
||||
val returnType = otherKind
|
||||
out.println("""@SinceKotlin("1.5")""")
|
||||
out.println("@kotlin.internal.InlineOnly")
|
||||
val declaration = "public inline fun ${thisKind.capitalized}.mod(other: ${otherKind.capitalized}): ${returnType.capitalized}"
|
||||
if (thisKind == otherKind && thisKind >= PrimitiveType.INT) {
|
||||
out.println(
|
||||
"""
|
||||
$declaration {
|
||||
val r = this % other
|
||||
return r + (other and (((r xor other) and (r or -r)) shr ${operationType.bitSize - 1}))
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
} else {
|
||||
out.println("$declaration = ")
|
||||
out.println(" " + convert(
|
||||
"${convert("this", thisKind, operationType)}.mod(${convert("other", otherKind, operationType)})",
|
||||
operationType, returnType
|
||||
))
|
||||
}
|
||||
out.println()
|
||||
}
|
||||
|
||||
private fun generateFpMod(thisKind: PrimitiveType, otherKind: PrimitiveType) {
|
||||
val operationType = getOperatorReturnType(thisKind, otherKind)
|
||||
out.println("""@SinceKotlin("1.5")""")
|
||||
out.println("@kotlin.internal.InlineOnly")
|
||||
val declaration = "public inline fun ${thisKind.capitalized}.mod(other: ${otherKind.capitalized}): ${operationType.capitalized}"
|
||||
if (thisKind == otherKind && thisKind >= PrimitiveType.INT) {
|
||||
out.println(
|
||||
"""
|
||||
$declaration {
|
||||
val r = this % other
|
||||
return if (r != ${convert("0.0", PrimitiveType.DOUBLE, operationType)} && r.sign != other.sign) r + other else r
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
} else {
|
||||
out.println("$declaration = ")
|
||||
out.println(" ${convert("this", thisKind, operationType)}.mod(${convert("other", otherKind, operationType)})")
|
||||
}
|
||||
out.println()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private fun maxByDomainCapacity(type1: PrimitiveType, type2: PrimitiveType): PrimitiveType
|
||||
= if (type1.ordinal > type2.ordinal) type1 else type2
|
||||
|
||||
private fun getOperatorReturnType(kind1: PrimitiveType, kind2: PrimitiveType): PrimitiveType {
|
||||
require(kind1 != PrimitiveType.BOOLEAN) { "kind1 must not be BOOLEAN" }
|
||||
require(kind2 != PrimitiveType.BOOLEAN) { "kind2 must not be BOOLEAN" }
|
||||
return maxByDomainCapacity(maxByDomainCapacity(kind1, kind2), PrimitiveType.INT)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.generators.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.generators.builtins.UnsignedType
|
||||
import org.jetbrains.kotlin.generators.builtins.convert
|
||||
import org.jetbrains.kotlin.generators.builtins.generateBuiltIns.BuiltInsSourceGenerator
|
||||
import org.jetbrains.kotlin.generators.builtins.ranges.GeneratePrimitives
|
||||
import org.jetbrains.kotlin.generators.builtins.numbers.GeneratePrimitives
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
|
||||
@@ -127,6 +127,8 @@ class UnsignedTypeGenerator(val type: UnsignedType, out: PrintWriter) : BuiltIns
|
||||
for ((name, doc) in GeneratePrimitives.binaryOperators) {
|
||||
generateOperator(name, doc)
|
||||
}
|
||||
generateFloorDivMod("floorDiv", "TODO")
|
||||
generateFloorDivMod("mod", "TODO")
|
||||
}
|
||||
|
||||
private fun generateOperator(name: String, doc: String) {
|
||||
@@ -150,6 +152,32 @@ class UnsignedTypeGenerator(val type: UnsignedType, out: PrintWriter) : BuiltIns
|
||||
out.println()
|
||||
}
|
||||
|
||||
private fun generateFloorDivMod(name: String, doc: String) {
|
||||
for (otherType in UnsignedType.values()) {
|
||||
val operationType = getOperatorReturnType(type, otherType)
|
||||
val returnType = if (name == "mod") otherType else operationType
|
||||
|
||||
out.println(" /** $doc */")
|
||||
out.println(" @kotlin.internal.InlineOnly")
|
||||
out.print(" public inline fun $name(other: ${otherType.capitalized}): ${returnType.capitalized} = ")
|
||||
if (type == otherType && type == operationType) {
|
||||
when (name) {
|
||||
"floorDiv" -> out.println("div(other)")
|
||||
"mod" -> out.println("rem(other)")
|
||||
else -> error(name)
|
||||
}
|
||||
} else {
|
||||
out.println(
|
||||
convert(
|
||||
"${convert("this", type, operationType)}.$name(${convert("other", otherType, operationType)})",
|
||||
operationType, returnType
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
out.println()
|
||||
}
|
||||
|
||||
|
||||
private fun generateUnaryOperators() {
|
||||
for ((name, doc) in GeneratePrimitives.unaryOperators) {
|
||||
|
||||
Reference in New Issue
Block a user