Encapsulate variable construction and declaration order logic within
ForLoopHeader instead of in HeaderProcessor.
This commit is contained in:
committed by
max-kammerer
parent
60c05362d2
commit
2ab539c5eb
+104
-122
@@ -53,31 +53,98 @@ internal interface ForLoopHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal abstract class NumericForLoopHeader(
|
internal abstract class NumericForLoopHeader(
|
||||||
protected open val headerInfo: HeaderInfo,
|
headerInfo: HeaderInfo,
|
||||||
val inductionVariable: IrVariable,
|
builder: DeclarationIrBuilder,
|
||||||
lastExpression: IrExpression,
|
protected val isLastInclusive: Boolean
|
||||||
val step: IrVariable,
|
|
||||||
val isLastInclusive: Boolean,
|
|
||||||
override val loopInitStatements: List<IrStatement>
|
|
||||||
) : ForLoopHeader {
|
) : ForLoopHeader {
|
||||||
|
|
||||||
val lastExpression: IrExpression = lastExpression
|
protected val inductionVariable: IrVariable
|
||||||
|
protected val lastVariableIfCanCacheLast: IrVariable?
|
||||||
|
protected val stepVariable: IrVariable
|
||||||
|
protected val lastExpression: IrExpression
|
||||||
// Always copy `lastExpression` is it may be used in multiple conditions.
|
// Always copy `lastExpression` is it may be used in multiple conditions.
|
||||||
get() = field.deepCopyWithSymbols()
|
get() = field.deepCopyWithSymbols()
|
||||||
|
|
||||||
|
init {
|
||||||
|
with(builder) {
|
||||||
|
// For this loop:
|
||||||
|
//
|
||||||
|
// for (i in first()..last() step step())
|
||||||
|
//
|
||||||
|
// We need to cast first(), last(). and step() to conform to the progression type so
|
||||||
|
// that operations on the induction variable within the loop are more efficient.
|
||||||
|
//
|
||||||
|
// In the above example, if first() is a Long and last() is an Int, this creates a
|
||||||
|
// LongProgression so last() should be cast to a Long.
|
||||||
|
inductionVariable = scope.createTemporaryVariable(
|
||||||
|
headerInfo.first.castIfNecessary(
|
||||||
|
headerInfo.progressionType.elementType(context.irBuiltIns),
|
||||||
|
headerInfo.progressionType.elementCastFunctionName
|
||||||
|
),
|
||||||
|
nameHint = "inductionVariable",
|
||||||
|
isMutable = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// TODO: Confirm if casting to non-nullable is still necessary
|
||||||
|
val last = ensureNotNullable(
|
||||||
|
headerInfo.last.castIfNecessary(
|
||||||
|
headerInfo.progressionType.elementType(context.irBuiltIns),
|
||||||
|
headerInfo.progressionType.elementCastFunctionName
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
lastVariableIfCanCacheLast = if (headerInfo.canCacheLast) {
|
||||||
|
scope.createTemporaryVariable(
|
||||||
|
last,
|
||||||
|
nameHint = "last"
|
||||||
|
)
|
||||||
|
} else null
|
||||||
|
|
||||||
|
lastExpression = if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else last
|
||||||
|
|
||||||
|
stepVariable = headerInfo.progressionType.stepType(context.irBuiltIns).let {
|
||||||
|
scope.createTemporaryVariable(
|
||||||
|
ensureNotNullable(
|
||||||
|
headerInfo.step.castIfNecessary(
|
||||||
|
it,
|
||||||
|
headerInfo.progressionType.stepCastFunctionName
|
||||||
|
)
|
||||||
|
),
|
||||||
|
nameHint = "step",
|
||||||
|
irType = it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This cannot be declared and initialized directly in the constructor. At the time of the base class (ForLoopHeader) constructor
|
||||||
|
// execution, the `headerInfo` property overridden in the derived class (e.g., NumericForLoopHeader) is not yet initialized.
|
||||||
|
// Therefore, we must use the `headerInfo` constructor parameter in the "init" block above instead of using the property.
|
||||||
|
protected open val headerInfo: HeaderInfo = headerInfo
|
||||||
|
|
||||||
|
private fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression) =
|
||||||
|
if (expression.type is IrSimpleType && expression.type.isNullable()) {
|
||||||
|
irImplicitCast(expression, expression.type.makeNotNull())
|
||||||
|
} else {
|
||||||
|
expression
|
||||||
|
}
|
||||||
|
|
||||||
/** Statement used to increment the induction variable. */
|
/** Statement used to increment the induction variable. */
|
||||||
protected fun incrementInductionVariable(builder: DeclarationIrBuilder): IrStatement = with(builder) {
|
protected fun incrementInductionVariable(builder: DeclarationIrBuilder): IrStatement = with(builder) {
|
||||||
// inductionVariable = inductionVariable + step
|
// inductionVariable = inductionVariable + step
|
||||||
val plusFun = inductionVariable.type.getClass()!!.functions.single {
|
val plusFun = inductionVariable.type.getClass()!!.functions.single {
|
||||||
it.name == OperatorNameConventions.PLUS &&
|
it.name == OperatorNameConventions.PLUS &&
|
||||||
it.valueParameters.size == 1 &&
|
it.valueParameters.size == 1 &&
|
||||||
it.valueParameters[0].type == step.type
|
it.valueParameters[0].type == stepVariable.type
|
||||||
}
|
}
|
||||||
irSetVar(
|
irSetVar(
|
||||||
inductionVariable.symbol, irCallOp(
|
inductionVariable.symbol, irCallOp(
|
||||||
plusFun.symbol, plusFun.returnType,
|
plusFun.symbol, plusFun.returnType,
|
||||||
irGet(inductionVariable),
|
irGet(inductionVariable),
|
||||||
irGet(step)
|
irGet(stepVariable)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -114,7 +181,7 @@ internal abstract class NumericForLoopHeader(
|
|||||||
context.oror(
|
context.oror(
|
||||||
context.andand(
|
context.andand(
|
||||||
irCall(builtIns.greaterFunByOperandType[stepType.classifierOrFail]!!).apply {
|
irCall(builtIns.greaterFunByOperandType[stepType.classifierOrFail]!!).apply {
|
||||||
putValueArgument(0, irGet(step))
|
putValueArgument(0, irGet(stepVariable))
|
||||||
putValueArgument(1, if (isLong) irLong(0) else irInt(0))
|
putValueArgument(1, if (isLong) irLong(0) else irInt(0))
|
||||||
},
|
},
|
||||||
irCall(compFun).apply {
|
irCall(compFun).apply {
|
||||||
@@ -123,7 +190,7 @@ internal abstract class NumericForLoopHeader(
|
|||||||
}),
|
}),
|
||||||
context.andand(
|
context.andand(
|
||||||
irCall(builtIns.lessFunByOperandType[stepType.classifierOrFail]!!).apply {
|
irCall(builtIns.lessFunByOperandType[stepType.classifierOrFail]!!).apply {
|
||||||
putValueArgument(0, irGet(step))
|
putValueArgument(0, irGet(stepVariable))
|
||||||
putValueArgument(1, if (isLong) irLong(0) else irInt(0))
|
putValueArgument(1, if (isLong) irLong(0) else irInt(0))
|
||||||
},
|
},
|
||||||
irCall(compFun).apply {
|
irCall(compFun).apply {
|
||||||
@@ -138,17 +205,25 @@ internal abstract class NumericForLoopHeader(
|
|||||||
|
|
||||||
internal class ProgressionLoopHeader(
|
internal class ProgressionLoopHeader(
|
||||||
override val headerInfo: ProgressionHeaderInfo,
|
override val headerInfo: ProgressionHeaderInfo,
|
||||||
inductionVariable: IrVariable,
|
builder: DeclarationIrBuilder
|
||||||
lastExpression: IrExpression,
|
) : NumericForLoopHeader(headerInfo, builder, isLastInclusive = true) {
|
||||||
step: IrVariable,
|
|
||||||
loopInitStatements: List<IrStatement>
|
// For this loop:
|
||||||
) : NumericForLoopHeader(
|
//
|
||||||
headerInfo, inductionVariable,
|
// for (i in first()..last() step step())
|
||||||
lastExpression = lastExpression,
|
//
|
||||||
step = step,
|
// ...the functions may have side-effects so we need to call them in the following order: first() (inductionVariable), last(), step().
|
||||||
isLastInclusive = true,
|
// Additional variables come first as they may be needed to the subsequent variables.
|
||||||
loopInitStatements = loopInitStatements
|
//
|
||||||
) {
|
// In the case of a reversed range, the `inductionVariable` and `last` variables are swapped, therefore the declaration order must be
|
||||||
|
// swapped to preserve the correct evaluation order.
|
||||||
|
override val loopInitStatements = headerInfo.additionalVariables + (
|
||||||
|
if (headerInfo.isReversed)
|
||||||
|
listOfNotNull(lastVariableIfCanCacheLast, inductionVariable)
|
||||||
|
else
|
||||||
|
listOfNotNull(inductionVariable, lastVariableIfCanCacheLast)
|
||||||
|
) +
|
||||||
|
stepVariable
|
||||||
|
|
||||||
private var loopVariable: IrVariable? = null
|
private var loopVariable: IrVariable? = null
|
||||||
|
|
||||||
@@ -209,15 +284,10 @@ internal class ProgressionLoopHeader(
|
|||||||
|
|
||||||
internal class IndexedGetLoopHeader(
|
internal class IndexedGetLoopHeader(
|
||||||
override val headerInfo: IndexedGetHeaderInfo,
|
override val headerInfo: IndexedGetHeaderInfo,
|
||||||
inductionVariable: IrVariable,
|
builder: DeclarationIrBuilder
|
||||||
lastExpression: IrExpression,
|
) : NumericForLoopHeader(headerInfo, builder, isLastInclusive = false) {
|
||||||
step: IrVariable,
|
|
||||||
loopInitStatements: List<IrStatement>
|
override val loopInitStatements = listOfNotNull(headerInfo.objectVariable, inductionVariable, lastVariableIfCanCacheLast, stepVariable)
|
||||||
) : NumericForLoopHeader(
|
|
||||||
headerInfo, inductionVariable, lastExpression, step,
|
|
||||||
isLastInclusive = false,
|
|
||||||
loopInitStatements = loopInitStatements
|
|
||||||
) {
|
|
||||||
|
|
||||||
override fun initializeIteration(loopVariable: IrVariable, symbols: Symbols<CommonBackendContext>, builder: DeclarationIrBuilder) =
|
override fun initializeIteration(loopVariable: IrVariable, symbols: Symbols<CommonBackendContext>, builder: DeclarationIrBuilder) =
|
||||||
with(builder) {
|
with(builder) {
|
||||||
@@ -292,97 +362,9 @@ internal class HeaderProcessor(
|
|||||||
?: return null // If the iterable is not supported.
|
?: return null // If the iterable is not supported.
|
||||||
|
|
||||||
val builder = context.createIrBuilder(scopeOwnerSymbol(), variable.startOffset, variable.endOffset)
|
val builder = context.createIrBuilder(scopeOwnerSymbol(), variable.startOffset, variable.endOffset)
|
||||||
with(builder) {
|
return when (headerInfo) {
|
||||||
// For this loop:
|
is IndexedGetHeaderInfo -> IndexedGetLoopHeader(headerInfo, builder)
|
||||||
//
|
is ProgressionHeaderInfo -> ProgressionLoopHeader(headerInfo, builder)
|
||||||
// for (i in first()..last() step step())
|
|
||||||
//
|
|
||||||
// We need to cast first(), last(). and step() to conform to the progression type so
|
|
||||||
// that operations on the induction variable within the loop are more efficient.
|
|
||||||
//
|
|
||||||
// In the above example, if first() is a Long and last() is an Int, this creates a
|
|
||||||
// LongProgression so last() should be cast to a Long.
|
|
||||||
val inductionVariable = scope.createTemporaryVariable(
|
|
||||||
headerInfo.first.castIfNecessary(
|
|
||||||
headerInfo.progressionType.elementType(context.irBuiltIns),
|
|
||||||
headerInfo.progressionType.elementCastFunctionName
|
|
||||||
),
|
|
||||||
nameHint = "inductionVariable",
|
|
||||||
isMutable = true
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
// TODO: Confirm if casting to non-nullable is still necessary
|
|
||||||
val lastExpression = ensureNotNullable(
|
|
||||||
headerInfo.last.castIfNecessary(
|
|
||||||
headerInfo.progressionType.elementType(context.irBuiltIns),
|
|
||||||
headerInfo.progressionType.elementCastFunctionName
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val lastVariableIfCanCacheLast = if (headerInfo.canCacheLast) {
|
|
||||||
scope.createTemporaryVariable(
|
|
||||||
lastExpression,
|
|
||||||
nameHint = "last"
|
|
||||||
)
|
|
||||||
} else null
|
|
||||||
|
|
||||||
val stepVariable = headerInfo.progressionType.stepType(context.irBuiltIns).let {
|
|
||||||
scope.createTemporaryVariable(
|
|
||||||
ensureNotNullable(
|
|
||||||
headerInfo.step.castIfNecessary(
|
|
||||||
it,
|
|
||||||
headerInfo.progressionType.stepCastFunctionName
|
|
||||||
)
|
|
||||||
),
|
|
||||||
nameHint = "step",
|
|
||||||
irType = it
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return when (headerInfo) {
|
|
||||||
is IndexedGetHeaderInfo -> IndexedGetLoopHeader(
|
|
||||||
headerInfo,
|
|
||||||
inductionVariable,
|
|
||||||
if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else lastExpression,
|
|
||||||
stepVariable,
|
|
||||||
listOfNotNull(headerInfo.objectVariable, inductionVariable, lastVariableIfCanCacheLast, stepVariable)
|
|
||||||
)
|
|
||||||
is ProgressionHeaderInfo -> {
|
|
||||||
// For this loop:
|
|
||||||
//
|
|
||||||
// for (i in first()..last() step step())
|
|
||||||
//
|
|
||||||
// ...the functions may have side-effects so we need to call them in the following order: first() (inductionVariable), last(), step().
|
|
||||||
// Additional variables come first as they may be needed to the subsequent variables.
|
|
||||||
//
|
|
||||||
// In the case of a reversed range, the `inductionVariable` and `last` variables are swapped, therefore the declaration order must be
|
|
||||||
// swapped to preserve the correct evaluation order.
|
|
||||||
val declarations = headerInfo.additionalVariables + (
|
|
||||||
if (headerInfo.isReversed)
|
|
||||||
listOfNotNull(lastVariableIfCanCacheLast, inductionVariable)
|
|
||||||
else
|
|
||||||
listOfNotNull(inductionVariable, lastVariableIfCanCacheLast)
|
|
||||||
) +
|
|
||||||
stepVariable
|
|
||||||
ProgressionLoopHeader(
|
|
||||||
headerInfo,
|
|
||||||
inductionVariable,
|
|
||||||
if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else lastExpression,
|
|
||||||
stepVariable,
|
|
||||||
declarations
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression) =
|
|
||||||
if (expression.type is IrSimpleType && expression.type.isNullable()) {
|
|
||||||
irImplicitCast(expression, expression.type.makeNotNull())
|
|
||||||
} else {
|
|
||||||
expression
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user