backend: Allow fake nullables in FOR loops
This commit is contained in:
+30
-12
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
@@ -73,14 +74,18 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo
|
||||
private val scopeOwnerSymbol
|
||||
get() = currentScope!!.scope.scopeOwnerSymbol
|
||||
|
||||
private val progressionElementClasses: Set<IrClassSymbol> = mutableSetOf(symbols.char).apply {
|
||||
private val progressionElementClasses: List<IrClassSymbol> = mutableListOf(symbols.char).apply {
|
||||
addAll(symbols.integerClasses)
|
||||
}
|
||||
|
||||
private val progressionElementClassesTypes: Set<SimpleType> = mutableSetOf<SimpleType>().apply {
|
||||
private val progressionElementClassesTypes: List<SimpleType> = mutableListOf<SimpleType>().apply {
|
||||
progressionElementClasses.mapTo(this) { it.descriptor.defaultType }
|
||||
}
|
||||
|
||||
private val progressionElementClassesNullableTypes: List<SimpleType> = mutableListOf<SimpleType>().apply {
|
||||
progressionElementClassesTypes.mapTo(this) { it.makeNullableAsSpecified(true) }
|
||||
}
|
||||
|
||||
//region Symbols for progression building functions ================================================================
|
||||
private fun getProgressionBuildingMethods(name: String): Set<IrFunctionSymbol> =
|
||||
getMethodsForProgressionElements(name) {
|
||||
@@ -128,13 +133,22 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo
|
||||
//endregion
|
||||
|
||||
//region Util methods ==============================================================================================
|
||||
private fun IrExpression.castIfNecessary(progressionType: ProgressionType, castToChar: Boolean = true): IrExpression {
|
||||
assert(type in progressionElementClassesTypes)
|
||||
if (type == progressionType.elementType || (!castToChar && KotlinBuiltIns.isChar(progressionType.elementType))) {
|
||||
return this
|
||||
private fun IrExpression.castIfNecessary(progressionType: ProgressionType): IrExpression {
|
||||
assert(type in progressionElementClassesTypes || type in progressionElementClassesNullableTypes)
|
||||
return if (type == progressionType.elementType) {
|
||||
this
|
||||
} else {
|
||||
IrCallImpl(startOffset, endOffset, symbols.getFunction(progressionType.numberCastFunctionName, type))
|
||||
.apply { dispatchReceiver = this@castIfNecessary }
|
||||
}
|
||||
}
|
||||
|
||||
private fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression): IrExpression {
|
||||
return if (expression.type.isMarkedNullable) {
|
||||
irImplicitCast(expression, expression.type.makeNotNullable())
|
||||
} else {
|
||||
expression
|
||||
}
|
||||
return IrCallImpl(startOffset, endOffset, symbols.getFunction(progressionType.numberCastFunctionName, type))
|
||||
.apply { dispatchReceiver = this@castIfNecessary }
|
||||
}
|
||||
|
||||
private fun IrExpression.unaryMinus(): IrExpression =
|
||||
@@ -163,8 +177,10 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo
|
||||
|
||||
// Used only by the assert.
|
||||
private fun stepHasRightType(step: IrExpression, progressionType: ProgressionType) =
|
||||
((progressionType.isCharProgression() || progressionType.isIntProgression()) && KotlinBuiltIns.isInt(step.type)) ||
|
||||
(progressionType.isLongProgression() && KotlinBuiltIns.isLong(step.type))
|
||||
((progressionType.isCharProgression() || progressionType.isIntProgression()) &&
|
||||
KotlinBuiltIns.isInt(step.type.makeNotNullable())) ||
|
||||
(progressionType.isLongProgression() &&
|
||||
KotlinBuiltIns.isLong(step.type.makeNotNullable()))
|
||||
|
||||
private fun irCheckProgressionStep(progressionType: ProgressionType,
|
||||
step: IrExpression): Pair<IrExpression, Boolean> {
|
||||
@@ -177,7 +193,7 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo
|
||||
// so there is no need to cast it.
|
||||
assert(stepHasRightType(step, progressionType))
|
||||
|
||||
val symbol = symbols.checkProgressionStep[step.type]
|
||||
val symbol = symbols.checkProgressionStep[step.type.makeNotNullable()]
|
||||
?: throw IllegalArgumentException("Unknown progression element type: ${step.type}")
|
||||
return IrCallImpl(step.startOffset, step.endOffset, symbol).apply {
|
||||
putValueArgument(0, step)
|
||||
@@ -307,6 +323,8 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo
|
||||
|
||||
with(builder) {
|
||||
with(progressionInfo) {
|
||||
// Due to features of PSI2IR we can obtain nullable arguments here while actually
|
||||
// they are non-nullable (the frontend takes care about this). So we need to cast them to non-nullable.
|
||||
val statements = mutableListOf<IrStatement>()
|
||||
|
||||
/**
|
||||
@@ -329,7 +347,7 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo
|
||||
|
||||
|
||||
val stepExpression = (if (increasing) step else step?.unaryMinus()) ?: defaultStep(startOffset, endOffset)
|
||||
val stepValue = scope.createTemporaryVariable(stepExpression,
|
||||
val stepValue = scope.createTemporaryVariable(ensureNotNullable(stepExpression),
|
||||
nameHint = "step",
|
||||
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE).also {
|
||||
statements.add(it)
|
||||
|
||||
+2
-2
@@ -234,8 +234,8 @@ private class Inliner(val globalSubstituteMap: MutableMap<DeclarationDescriptor,
|
||||
}
|
||||
|
||||
private fun isLambdaExpression(expression: IrExpression) : Boolean {
|
||||
if (expression !is IrBlock) return false // Lambda mast be represented with IrBlock.
|
||||
if (expression.origin != IrStatementOrigin.LAMBDA // Origin mast be LAMBDA or ANONYMOUS.
|
||||
if (expression !is IrBlock) return false // Lambda must be represented with IrBlock.
|
||||
if (expression.origin != IrStatementOrigin.LAMBDA // Origin must be LAMBDA or ANONYMOUS.
|
||||
&& expression.origin != IrStatementOrigin.ANONYMOUS_FUNCTION) return false
|
||||
|
||||
val statements = expression.statements
|
||||
|
||||
@@ -400,6 +400,13 @@ task codegen_controlflow_for_loops_coroutines(type: RunKonanTest) {
|
||||
"Got: 0 2 4 6\n"
|
||||
}
|
||||
|
||||
task codegen_controlflow_for_loops_let_with_nullable(type: RunKonanTest) {
|
||||
source = "codegen/controlflow/for_loops_let_with_nullable.kt"
|
||||
goldValue = "012345\n012345\n024\n01234\n01234\n024\n543210\n543210\n531\n" +
|
||||
"012345\n012345\n024\n01234\n01234\n024\n543210\n543210\n531\n" +
|
||||
"abcdef\nabcdef\nace\nabcde\nabcde\nace\nfedcba\nfedcba\nfdb\n"
|
||||
}
|
||||
|
||||
task codegen_controlflow_for_loops_call_order(type: RunKonanTest) {
|
||||
source = "codegen/controlflow/for_loops_call_order.kt"
|
||||
goldValue = "1234\n1234\n2134\n"
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
package codegen.controlflow.for_loops_let_with_nullable
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
// Github issue #1012
|
||||
fun testInt(left: Int?, right: Int?, step: Int?) {
|
||||
right?.let {
|
||||
for (i in 0..it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
left?.let {
|
||||
for (i in it..5) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
step?.let {
|
||||
for (i in 0..5 step it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
right?.let {
|
||||
for (i in 0 until it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
left?.let {
|
||||
for (i in it until 5) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
step?.let {
|
||||
for (i in 0 until 5 step it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
right?.let {
|
||||
for (i in it downTo 0) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
left?.let {
|
||||
for (i in 5 downTo it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
step?.let {
|
||||
for (i in 5 downTo 0 step it) { print(i) }
|
||||
}
|
||||
println()
|
||||
}
|
||||
|
||||
fun testLong(left: Long?, right: Long?, step: Long?) {
|
||||
right?.let {
|
||||
for (i in 0..it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
left?.let {
|
||||
for (i in it..5) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
step?.let {
|
||||
for (i in 0..5L step it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
right?.let {
|
||||
for (i in 0 until it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
left?.let {
|
||||
for (i in it until 5) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
step?.let {
|
||||
for (i in 0 until 5L step it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
right?.let {
|
||||
for (i in it downTo 0) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
left?.let {
|
||||
for (i in 5 downTo it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
step?.let {
|
||||
for (i in 5 downTo 0L step it) { print(i) }
|
||||
}
|
||||
println()
|
||||
}
|
||||
|
||||
fun testChar(left: Char?, right: Char?, step: Int?) {
|
||||
right?.let {
|
||||
for (i in 'a'..it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
left?.let {
|
||||
for (i in it..'f') { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
step?.let {
|
||||
for (i in 'a'..'f' step it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
right?.let {
|
||||
for (i in 'a' until it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
left?.let {
|
||||
for (i in it until 'f') { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
step?.let {
|
||||
for (i in 'a' until 'f' step it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
right?.let {
|
||||
for (i in it downTo 'a') { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
left?.let {
|
||||
for (i in 'f' downTo it) { print(i) }
|
||||
}
|
||||
println()
|
||||
|
||||
step?.let {
|
||||
for (i in 'f' downTo 'a' step it) { print(i) }
|
||||
}
|
||||
println()
|
||||
}
|
||||
|
||||
@Test fun runTest() {
|
||||
testInt(0, 5, 2)
|
||||
testLong(0, 5, 2)
|
||||
testChar('a', 'f', 2)
|
||||
}
|
||||
Reference in New Issue
Block a user