Handle for (i in a until Int.MIN_VALUE) corner case by adding

an additional "not empty" condition produced by the UntilHandler.
This commit is contained in:
Mark Punzalan
2019-03-19 23:16:09 -07:00
committed by max-kammerer
parent 7b153b2b68
commit 8e1be5424f
7 changed files with 71 additions and 13 deletions
@@ -81,7 +81,8 @@ internal class ProgressionHeaderInfo(
lowerBound: IrExpression,
upperBound: IrExpression,
step: IrExpression,
closed: Boolean = true
closed: Boolean = true,
val additionalNotEmptyCondition: IrExpression? = null
) : HeaderInfo(progressionType, lowerBound, upperBound, step, closed)
internal class ArrayHeaderInfo(
@@ -80,8 +80,7 @@ internal class ProgressionLoopHeader(
val progressionKotlinType = progressionType.elementType(builtIns).toKotlinType()
val lessOrEqualFun = builtIns.lessOrEqualFunByOperandType[progressionKotlinType]!!
// TODO: Additional condition for `for (i in a until MIN_VALUE)` corner case
when (headerInfo.direction) {
val notEmptyCondition = when (headerInfo.direction) {
ProgressionDirection.DECREASING ->
// last <= inductionVariable
irCall(lessOrEqualFun).apply {
@@ -121,6 +120,11 @@ internal class ProgressionLoopHeader(
)
}
}
if (headerInfo.additionalNotEmptyCondition != null) context.andand(
headerInfo.additionalNotEmptyCondition,
notEmptyCondition
) else notEmptyCondition
}
}
@@ -6,15 +6,15 @@
package org.jetbrains.kotlin.backend.common.lower.loops
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.backend.common.lower.matchers.createIrCallMatcher
import org.jetbrains.kotlin.backend.common.lower.matchers.singleArgumentExtension
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.classifierOrNull
@@ -74,14 +74,54 @@ internal class UntilHandler(private val context: CommonBackendContext, private v
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? =
with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) {
//
// An additional emptiness condition is required for the corner case:
//
// ```
// for (i in a until MIN_VALUE) {}
// ```
//
// ...which should always be considered an empty range. When the given bound is MIN_VALUE, and because `last = bound - 1`,
// "last" will underflow to MAX_VALUE, therefore the default emptiness check:
//
// ```
// if (first <= last) { /* loop */ }
// ```
//
// ...will always be true and won't consider the range as empty. Therefore, we need to add an additional condition to the
// emptiness check so that it becomes:
//
// ```
// if (first <= last && bound > MIN_VALUE) { /* loop */ }
// ```
//
// TODO: Do not add additionalEmptinessCondition if "bound" is const and > MIN_VALUE
ProgressionHeaderInfo(
data,
lowerBound = call.extensionReceiver!!,
upperBound = call.getValueArgument(0)!!,
step = irInt(1),
closed = false
closed = false,
additionalNotEmptyCondition = buildMinValueConditionIfNecessary(data, call.getValueArgument(0)!!)
)
}
private fun DeclarationIrBuilder.buildMinValueConditionIfNecessary(
progressionType: ProgressionType,
bound: IrExpression
): IrExpression? {
val irBuiltIns = context.irBuiltIns
val minConst = when (progressionType) {
ProgressionType.INT_PROGRESSION -> irInt(Int.MIN_VALUE)
ProgressionType.CHAR_PROGRESSION -> irChar(Char.MIN_VALUE)
ProgressionType.LONG_PROGRESSION -> irLong(Long.MIN_VALUE)
}
val progressionKotlinType = progressionType.elementType(irBuiltIns).toKotlinType()
return irCall(irBuiltIns.greaterFunByOperandType[progressionKotlinType]!!).apply {
putValueArgument(0, bound)
putValueArgument(1, minConst)
}
}
}
internal class IndicesHandler(val context: CommonBackendContext) : ProgressionHandler {
@@ -111,6 +151,22 @@ internal class IndicesHandler(val context: CommonBackendContext) : ProgressionHa
)
}
private fun DeclarationIrBuilder.buildMinValueConditionIfNecessary(
progressionType: ProgressionType,
bound: IrExpression
): IrExpression? {
val irBuiltIns = context.irBuiltIns
val minConst = when (progressionType) {
ProgressionType.INT_PROGRESSION -> irInt(Int.MIN_VALUE)
ProgressionType.CHAR_PROGRESSION -> irChar(Char.MIN_VALUE)
ProgressionType.LONG_PROGRESSION -> irLong(Long.MIN_VALUE)
}
val progressionKotlinType = progressionType.elementType(irBuiltIns).toKotlinType()
return irCall(irBuiltIns.greaterFunByOperandType[progressionKotlinType]!!).apply {
putValueArgument(0, bound)
putValueArgument(1, minConst)
}
}
}
internal class ArrayIterationHandler(val context: CommonBackendContext) : HeaderInfoHandler<Nothing?> {
@@ -282,6 +282,9 @@ fun IrBuilderWithScope.irInt(value: Int) =
fun IrBuilderWithScope.irLong(value: Long) =
IrConstImpl.long(startOffset, endOffset, context.irBuiltIns.longType, value)
fun IrBuilderWithScope.irChar(value: Char) =
IrConstImpl.char(startOffset, endOffset, context.irBuiltIns.charType, value)
fun IrBuilderWithScope.irString(value: String) =
IrConstImpl.string(startOffset, endOffset, context.irBuiltIns.stringType, value)
@@ -1,5 +1,3 @@
// TODO: Enable after corner case is addressed
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,5 +1,3 @@
// TODO: Enable after corner case is addressed
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,5 +1,3 @@
// TODO: Enable after corner case is addressed
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
import kotlin.test.assertEquals