[IR] Support if and when for MFVC, fix several bugs, refactor
#KT-1179
This commit is contained in:
committed by
teamcity
parent
70293fab60
commit
f8aa3612f8
+6
@@ -50195,6 +50195,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/valueClasses/complex.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
runTest("compiler/testData/codegen/box/valueClasses/complex.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("conditionalExpressions.kt")
|
||||||
|
public void testConditionalExpressions() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/valueClasses/conditionalExpressions.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("equality.kt")
|
@TestMetadata("equality.kt")
|
||||||
public void testEquality() throws Exception {
|
public void testEquality() throws Exception {
|
||||||
|
|||||||
+6
@@ -5789,6 +5789,12 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest {
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("conditionalExpressions.kt")
|
||||||
|
public void testConditionalExpressions() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/valueClasses/conditionalExpressions.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("equalsBoxTest.kt")
|
@TestMetadata("equalsBoxTest.kt")
|
||||||
public void testEqualsBoxTest() throws Exception {
|
public void testEqualsBoxTest() throws Exception {
|
||||||
|
|||||||
+170
-109
@@ -23,6 +23,8 @@ import org.jetbrains.kotlin.ir.builders.*
|
|||||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
|
||||||
@@ -93,14 +95,14 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
return expressions.subList(0, expressions.size - repeatable.size) to repeatable
|
return expressions.subList(0, expressions.size - repeatable.size) to repeatable
|
||||||
}
|
}
|
||||||
|
|
||||||
fun makeReplacement(scope: IrBuilderWithScope, expression: IrSetValue, safe: Boolean): IrExpression? = with(scope) {
|
fun addReplacement(scope: IrBlockBuilder, expression: IrSetValue, safe: Boolean): IrExpression? = with(scope) {
|
||||||
oldValueSymbol2NewValueSymbol[expression.symbol]?.let { return irSet(it.owner, expression.value) }
|
oldValueSymbol2NewValueSymbol[expression.symbol]?.let { return irSet(it.owner, expression.value) }
|
||||||
val instance = oldSymbol2MfvcNodeInstance[expression.symbol] ?: return@with null
|
val instance = oldSymbol2MfvcNodeInstance[expression.symbol] ?: return@with null
|
||||||
var values: List<IrExpression>? = null
|
val values: List<IrExpression> = makeFlattenedExpressionsWithGivenSafety(instance.node, safe, expression.value)
|
||||||
return irBlock {
|
val setterExpressions = instance.makeSetterExpressions(values)
|
||||||
values = makeFlattenedExpressionsWithGivenSafety(instance.node, safe, expression.value)
|
expression2MfvcNodeInstanceAccessor[setterExpressions] = MfvcNodeInstanceAccessor.Setter(instance, values)
|
||||||
instance.addSetterStatements(this, values!!)
|
+setterExpressions
|
||||||
}.also { expression2MfvcNodeInstanceAccessor[it] = MfvcNodeInstanceAccessor.Setter(instance, values!!) }
|
return setterExpressions
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,50 +126,52 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
private val IrFieldAccessExpression.field: IrField
|
private val IrFieldAccessExpression.field: IrField
|
||||||
get() = this.symbol.owner
|
get() = this.symbol.owner
|
||||||
|
|
||||||
fun makeReplacement(scope: IrBuilderWithScope, expression: IrGetField): IrExpression? {
|
fun addReplacement(scope: IrBlockBuilder, expression: IrGetField): IrExpression? {
|
||||||
val property = expression.field.property ?: return null
|
val property = expression.field.property ?: return null
|
||||||
expression.receiver?.get(property.name)?.let { return it }
|
expression.receiver?.get(property.name)?.let { scope.run { +it }; return it }
|
||||||
val node = replacements.getMfvcPropertyNode(property) ?: return null
|
val node = replacements.getMfvcPropertyNode(property) ?: return null
|
||||||
val typeArguments = makeTypeArgumentsFromField(expression)
|
val typeArguments = makeTypeArgumentsFromField(expression)
|
||||||
var instance: ReceiverBasedMfvcNodeInstance? = null
|
val instance: ReceiverBasedMfvcNodeInstance =
|
||||||
return scope.irBlock {
|
node.createInstanceFromBox(scope, typeArguments, expression.receiver, AccessType.AlwaysPrivate, ::variablesSaver)
|
||||||
instance = node.createInstanceFromBox(this, typeArguments, expression.receiver, AccessType.AlwaysPrivate, ::variablesSaver)
|
val getterExpression = instance.makeGetterExpression()
|
||||||
+instance!!.makeGetterExpression()
|
expression2MfvcNodeInstanceAccessor[getterExpression] = MfvcNodeInstanceAccessor.Getter(instance)
|
||||||
}.also { expression2MfvcNodeInstanceAccessor[it] = MfvcNodeInstanceAccessor.Getter(instance!!) }
|
scope.run { +getterExpression }
|
||||||
|
return getterExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
fun makeReplacement(scope: IrBuilderWithScope, expression: IrSetField, safe: Boolean): IrExpression? {
|
fun addReplacement(scope: IrBlockBuilder, expression: IrSetField, safe: Boolean): IrExpression? {
|
||||||
val property = expression.field.property ?: return null
|
val property = expression.field.property ?: return null
|
||||||
expression.receiver?.get(property.name)?.let { return it }
|
expression.receiver?.get(property.name)?.let { scope.run { +it }; return it }
|
||||||
val node = replacements.getMfvcPropertyNode(property) ?: return null
|
val node = replacements.getMfvcPropertyNode(property) ?: return null
|
||||||
val typeArguments = makeTypeArgumentsFromField(expression)
|
val typeArguments = makeTypeArgumentsFromField(expression)
|
||||||
var instance: ReceiverBasedMfvcNodeInstance? = null
|
val instance: ReceiverBasedMfvcNodeInstance =
|
||||||
var values: List<IrExpression>? = null
|
node.createInstanceFromBox(scope, typeArguments, expression.receiver, AccessType.AlwaysPrivate, ::variablesSaver)
|
||||||
return scope.irBlock {
|
val values: List<IrExpression> = scope.makeFlattenedExpressionsWithGivenSafety(node, safe, expression.value)
|
||||||
instance = node.createInstanceFromBox(this, typeArguments, expression.receiver, AccessType.AlwaysPrivate, ::variablesSaver)
|
val setterExpressions = instance.makeSetterExpressions(values)
|
||||||
values = makeFlattenedExpressionsWithGivenSafety(node, safe, expression.value)
|
expression2MfvcNodeInstanceAccessor[setterExpressions] = MfvcNodeInstanceAccessor.Setter(instance, values)
|
||||||
instance!!.addSetterStatements(this, values!!)
|
scope.run { +setterExpressions }
|
||||||
}.also { expression2MfvcNodeInstanceAccessor[it] = MfvcNodeInstanceAccessor.Setter(instance!!, values!!) }
|
return setterExpressions
|
||||||
}
|
}
|
||||||
|
|
||||||
fun makeReplacement(scope: IrBuilderWithScope, expression: IrCall): IrExpression? {
|
fun addReplacement(scope: IrBlockBuilder, expression: IrCall): IrExpression? {
|
||||||
val function = expression.symbol.owner
|
val function = expression.symbol.owner
|
||||||
val property = function.property?.takeIf { function.isGetter } ?: return null
|
val property = function.property?.takeIf { function.isGetter } ?: return null
|
||||||
val dispatchReceiver = expression.dispatchReceiver
|
val dispatchReceiver = expression.dispatchReceiver
|
||||||
dispatchReceiver?.get(property.name)?.let { return it }
|
dispatchReceiver?.get(property.name)?.let { scope.run { +it }; return it }
|
||||||
val node = replacements.getMfvcPropertyNode(property) ?: return null
|
val node = replacements.getMfvcPropertyNode(property) ?: return null
|
||||||
val typeArguments = makeTypeArgumentsFromFunction(expression)
|
val typeArguments = makeTypeArgumentsFromFunction(expression)
|
||||||
var instance: ReceiverBasedMfvcNodeInstance? = null
|
// Optimization: pure function access to leaf can be replaced with field access if the field itself is accessible
|
||||||
return scope.irBlock {
|
val accessType = when {
|
||||||
// Optimization: pure function access to leaf can be replaced with field access if the field itself is accessible
|
!node.hasPureUnboxMethod -> AccessType.AlwaysPublic
|
||||||
val accessType = when {
|
dispatchReceiver == null -> AccessType.PrivateWhenNoBox
|
||||||
!node.hasPureUnboxMethod -> AccessType.AlwaysPublic
|
else -> getOptimizedPublicAccess(dispatchReceiver.type.erasedUpperBound)
|
||||||
dispatchReceiver == null -> AccessType.PrivateWhenNoBox
|
}
|
||||||
else -> getOptimizedPublicAccess(dispatchReceiver.type.erasedUpperBound)
|
val instance: ReceiverBasedMfvcNodeInstance =
|
||||||
}
|
node.createInstanceFromBox(scope, typeArguments, dispatchReceiver, accessType, ::variablesSaver)
|
||||||
instance = node.createInstanceFromBox(this, typeArguments, dispatchReceiver, accessType, ::variablesSaver)
|
val getterExpression = instance.makeGetterExpression()
|
||||||
+instance!!.makeGetterExpression()
|
expression2MfvcNodeInstanceAccessor[getterExpression] = MfvcNodeInstanceAccessor.Getter(instance)
|
||||||
}.also { expression2MfvcNodeInstanceAccessor[it] = MfvcNodeInstanceAccessor.Getter(instance!!) }
|
scope.run { +getterExpression }
|
||||||
|
return getterExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
private val IrField.property
|
private val IrField.property
|
||||||
@@ -187,25 +191,51 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
expression.dispatchReceiver?.type?.let { putAll(makeTypeArgumentsFromType(it as IrSimpleType)) }
|
expression.dispatchReceiver?.type?.let { putAll(makeTypeArgumentsFromType(it as IrSimpleType)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun IrExpression.get(name: Name): IrExpression? {
|
private fun handleSavedExpression(
|
||||||
val accessor = getMfvcNodeInstanceAccessor(this) ?: return null
|
expression: IrExpression, handler: IrBlockBuilder.(accessor: MfvcNodeInstanceAccessor) -> Unit?
|
||||||
val newAccessor = accessor[name] ?: return null
|
): IrExpression? {
|
||||||
|
val accessor = expression2MfvcNodeInstanceAccessor[expression]
|
||||||
|
return when {
|
||||||
|
accessor != null -> accessor.instance.scope.irBlock { handler(accessor) ?: return null }
|
||||||
|
expression !is IrContainerExpression -> null
|
||||||
|
else -> when (val lastExpression = expression.statements.lastOrNull()) {
|
||||||
|
is IrExpression -> {
|
||||||
|
val inner = handleSavedExpression(lastExpression, handler) ?: return null
|
||||||
|
if (expression.isTransparentScope) IrCompositeImpl(
|
||||||
|
startOffset = expression.startOffset,
|
||||||
|
endOffset = expression.endOffset,
|
||||||
|
type = inner.type,
|
||||||
|
origin = expression.origin,
|
||||||
|
statements = expression.statements.dropLast(1) + inner,
|
||||||
|
) else IrBlockImpl(
|
||||||
|
startOffset = expression.startOffset,
|
||||||
|
endOffset = expression.endOffset,
|
||||||
|
type = inner.type,
|
||||||
|
origin = expression.origin,
|
||||||
|
statements = expression.statements.dropLast(1) + inner,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun IrExpression.get(name: Name): IrExpression? = handleSavedExpression(this) { accessor ->
|
||||||
|
val newAccessor = accessor[name] ?: return@handleSavedExpression null
|
||||||
val expression = when (newAccessor) {
|
val expression = when (newAccessor) {
|
||||||
is MfvcNodeInstanceAccessor.Getter -> newAccessor.instance.makeGetterExpression()
|
is MfvcNodeInstanceAccessor.Getter -> newAccessor.instance.makeGetterExpression()
|
||||||
is MfvcNodeInstanceAccessor.Setter -> newAccessor.instance.makeSetterExpressions(newAccessor.values)
|
is MfvcNodeInstanceAccessor.Setter -> newAccessor.instance.makeSetterExpressions(newAccessor.values)
|
||||||
}
|
}
|
||||||
expression2MfvcNodeInstanceAccessor[expression] = newAccessor
|
expression2MfvcNodeInstanceAccessor[expression] = newAccessor
|
||||||
return expression
|
+expression
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMfvcNodeInstanceAccessor(expression: IrExpression): MfvcNodeInstanceAccessor? =
|
fun handleFlattenedGetterExpressions(
|
||||||
expression2MfvcNodeInstanceAccessor[expression]
|
expression: IrExpression,
|
||||||
|
handler: IrBlockBuilder.(values: List<IrExpression>) -> Unit
|
||||||
fun getMfvcNodeInstanceIfIsGetter(expression: IrExpression): MfvcNodeInstance? =
|
): IrExpression? =
|
||||||
getMfvcNodeInstanceAccessor(expression)?.takeIf { it is MfvcNodeInstanceAccessor.Getter }?.instance
|
handleSavedExpression(expression) { handler(it.instance.makeFlattenedGetterExpressions()) }
|
||||||
|
|
||||||
fun getMfvcNodeInstanceIfIsSetter(expression: IrExpression): MfvcNodeInstance? =
|
|
||||||
getMfvcNodeInstanceAccessor(expression)?.takeIf { it is MfvcNodeInstanceAccessor.Setter }?.instance
|
|
||||||
|
|
||||||
fun registerReplacement(expression: IrExpression, instance: MfvcNodeInstance) {
|
fun registerReplacement(expression: IrExpression, instance: MfvcNodeInstance) {
|
||||||
expression2MfvcNodeInstanceAccessor[expression] = MfvcNodeInstanceAccessor.Getter(instance)
|
expression2MfvcNodeInstanceAccessor[expression] = MfvcNodeInstanceAccessor.Getter(instance)
|
||||||
@@ -375,7 +405,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
private object UNSAFE_MFVC_SET_ORIGIN : IrStatementOrigin
|
private object UNSAFE_MFVC_SET_ORIGIN : IrStatementOrigin
|
||||||
|
|
||||||
private fun RootMfvcNode.replaceFields() {
|
private fun RootMfvcNode.replaceFields() {
|
||||||
mfvc.declarations.removeIf { it is IrField }
|
mfvc.declarations.removeIf { it is IrField && (!it.isStatic || it.type.needsMfvcFlattening()) }
|
||||||
mfvc.declarations += fields
|
mfvc.declarations += fields
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -579,7 +609,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
when (val structure = parametersStructure[i]) {
|
when (val structure = parametersStructure[i]) {
|
||||||
is MultiFieldValueClassMapping -> {
|
is MultiFieldValueClassMapping -> {
|
||||||
val mfvcNodeInstance = structure.rootMfvcNode.createInstanceFromValueDeclarationsAndBoxType(
|
val mfvcNodeInstance = structure.rootMfvcNode.createInstanceFromValueDeclarationsAndBoxType(
|
||||||
scope, structure.boxedType, newParamList, listOf()
|
scope, structure.boxedType, newParamList
|
||||||
)
|
)
|
||||||
valueDeclarationsRemapper.registerReplacement(param, mfvcNodeInstance)
|
valueDeclarationsRemapper.registerReplacement(param, mfvcNodeInstance)
|
||||||
}
|
}
|
||||||
@@ -598,7 +628,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
val typeArguments = makeTypeParameterSubstitutionMap(mfvc, primaryConstructorImpl)
|
val typeArguments = makeTypeParameterSubstitutionMap(mfvc, primaryConstructorImpl)
|
||||||
primaryConstructorImpl.body = context.createIrBuilder(primaryConstructorImpl.symbol).irBlockBody {
|
primaryConstructorImpl.body = context.createIrBuilder(primaryConstructorImpl.symbol).irBlockBody {
|
||||||
val mfvcNodeInstance =
|
val mfvcNodeInstance =
|
||||||
ValueDeclarationMfvcNodeInstance(this, rootMfvcNode, typeArguments, primaryConstructorImpl.valueParameters, listOf())
|
ValueDeclarationMfvcNodeInstance(this, rootMfvcNode, typeArguments, primaryConstructorImpl.valueParameters)
|
||||||
valueDeclarationsRemapper.registerReplacement(
|
valueDeclarationsRemapper.registerReplacement(
|
||||||
oldPrimaryConstructor.constructedClass.thisReceiver!!,
|
oldPrimaryConstructor.constructedClass.thisReceiver!!,
|
||||||
mfvcNodeInstance
|
mfvcNodeInstance
|
||||||
@@ -626,15 +656,17 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
return when {
|
return when {
|
||||||
function is IrConstructor && function.isPrimary && function.constructedClass.isMultiFieldValueClass &&
|
function is IrConstructor && function.isPrimary && function.constructedClass.isMultiFieldValueClass &&
|
||||||
currentScope.origin != JvmLoweredDeclarationOrigin.SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER -> {
|
currentScope.origin != JvmLoweredDeclarationOrigin.SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER -> {
|
||||||
var instance: MfvcNodeInstance? = null
|
|
||||||
context.createIrBuilder(currentScope.symbol).irBlock {
|
context.createIrBuilder(currentScope.symbol).irBlock {
|
||||||
|
val instance: MfvcNodeInstance
|
||||||
val rootNode = replacements.getRootMfvcNode(function.constructedClass)!!
|
val rootNode = replacements.getRootMfvcNode(function.constructedClass)!!
|
||||||
instance = rootNode.createInstanceFromValueDeclarationsAndBoxType(
|
instance = rootNode.createInstanceFromValueDeclarationsAndBoxType(
|
||||||
this, function.constructedClassType as IrSimpleType, Name.identifier("constructor_tmp"), ::variablesSaver
|
this, function.constructedClassType as IrSimpleType, Name.identifier("constructor_tmp"), ::variablesSaver
|
||||||
)
|
)
|
||||||
flattenExpressionTo(expression, instance!!)
|
flattenExpressionTo(expression, instance)
|
||||||
+instance!!.makeGetterExpression()
|
val getterExpression = instance.makeGetterExpression()
|
||||||
}.also { valueDeclarationsRemapper.registerReplacement(it, instance!!) }
|
valueDeclarationsRemapper.registerReplacement(getterExpression, instance)
|
||||||
|
+getterExpression
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
replacement != null -> context.createIrBuilder(currentScope.symbol).irBlock {
|
replacement != null -> context.createIrBuilder(currentScope.symbol).irBlock {
|
||||||
@@ -663,7 +695,9 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
) {
|
) {
|
||||||
require(callee.valueParameters.isEmpty()) { "Unexpected getter:\n${callee.dump()}" }
|
require(callee.valueParameters.isEmpty()) { "Unexpected getter:\n${callee.dump()}" }
|
||||||
expression.dispatchReceiver = expression.dispatchReceiver?.transform(this, null)
|
expression.dispatchReceiver = expression.dispatchReceiver?.transform(this, null)
|
||||||
return valueDeclarationsRemapper.makeReplacement(context.createIrBuilder(getCurrentScopeSymbol()), expression) ?: expression
|
return context.createIrBuilder(getCurrentScopeSymbol()).irBlock {
|
||||||
|
valueDeclarationsRemapper.addReplacement(this, expression) ?: return expression
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (expression.isSpecializedMFVCEqEq) {
|
if (expression.isSpecializedMFVCEqEq) {
|
||||||
return context.createIrBuilder(getCurrentScopeSymbol()).irBlock {
|
return context.createIrBuilder(getCurrentScopeSymbol()).irBlock {
|
||||||
@@ -826,31 +860,49 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
val forbiddenVariables = mutableSetOf<IrVariable>()
|
val forbiddenVariables = mutableSetOf<IrVariable>()
|
||||||
val variablesSet = variables.toSet()
|
val variablesSet = variables.toSet()
|
||||||
val standaloneExpressions = mutableListOf<IrExpression>()
|
val standaloneExpressions = mutableListOf<IrExpression>()
|
||||||
for ((expression, variable) in block.statements.asReversed() zip variables.asReversed()) {
|
val resultVariables = variables.toMutableList()
|
||||||
when {
|
|
||||||
expression !is IrSetValue -> break
|
|
||||||
expression.symbol.owner != variable -> break
|
|
||||||
expression.symbol.owner in forbiddenVariables -> break
|
|
||||||
else -> {
|
|
||||||
standaloneExpressions.add(expression.value)
|
|
||||||
expression.value.acceptVoid(object : IrElementVisitorVoid {
|
|
||||||
override fun visitExpression(expression: IrExpression) {
|
|
||||||
expression.acceptChildrenVoid(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitValueAccess(expression: IrValueAccessExpression) {
|
fun recur(block: IrContainerExpression): Boolean /* stop optimization */ {
|
||||||
val valueDeclaration = expression.symbol.owner
|
while (block.statements.isNotEmpty() && resultVariables.isNotEmpty()) {
|
||||||
if (valueDeclaration is IrVariable && valueDeclaration in variablesSet) {
|
val statement = block.statements.last()
|
||||||
forbiddenVariables.add(valueDeclaration)
|
//also stop
|
||||||
}
|
when {
|
||||||
super.visitValueAccess(expression)
|
statement is IrContainerExpression -> if (recur(statement)) {
|
||||||
|
if (statement.statements.isEmpty()) {
|
||||||
|
block.statements.removeLast()
|
||||||
}
|
}
|
||||||
})
|
return true
|
||||||
|
} else {
|
||||||
|
require(statement.statements.isEmpty() || resultVariables.isEmpty()) { "Not all statements removed" }
|
||||||
|
}
|
||||||
|
|
||||||
|
statement !is IrSetValue -> return true
|
||||||
|
statement.symbol.owner != resultVariables.last() -> return true
|
||||||
|
statement.symbol.owner in forbiddenVariables -> return true
|
||||||
|
else -> {
|
||||||
|
standaloneExpressions.add(statement.value)
|
||||||
|
resultVariables.removeLast()
|
||||||
|
block.statements.removeLast()
|
||||||
|
statement.value.acceptVoid(object : IrElementVisitorVoid {
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitValueAccess(expression: IrValueAccessExpression) {
|
||||||
|
val valueDeclaration = expression.symbol.owner
|
||||||
|
if (valueDeclaration is IrVariable && valueDeclaration in variablesSet) {
|
||||||
|
forbiddenVariables.add(valueDeclaration)
|
||||||
|
}
|
||||||
|
super.visitValueAccess(expression)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
repeat(standaloneExpressions.size) { block.statements.removeLast() }
|
recur(block)
|
||||||
return variables.dropLast(standaloneExpressions.size).map { irGet(it) } + standaloneExpressions.asReversed()
|
return resultVariables.map { irGet(it) } + standaloneExpressions.asReversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that reference equality (x === y) is not allowed on values of MFVC class type,
|
// Note that reference equality (x === y) is not allowed on values of MFVC class type,
|
||||||
@@ -862,27 +914,26 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
|
|
||||||
override fun visitGetField(expression: IrGetField): IrExpression {
|
override fun visitGetField(expression: IrGetField): IrExpression {
|
||||||
expression.receiver = expression.receiver?.transform(this, null)
|
expression.receiver = expression.receiver?.transform(this, null)
|
||||||
return valueDeclarationsRemapper.makeReplacement(context.createIrBuilder(expression.symbol), expression)
|
return context.createIrBuilder(expression.symbol).irBlock {
|
||||||
?: expression
|
valueDeclarationsRemapper.addReplacement(this, expression) ?: return expression
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitSetField(expression: IrSetField): IrExpression {
|
override fun visitSetField(expression: IrSetField): IrExpression {
|
||||||
expression.receiver = expression.receiver?.transform(this, null)
|
expression.receiver = expression.receiver?.transform(this, null)
|
||||||
return valueDeclarationsRemapper.makeReplacement(
|
return context.createIrBuilder(getCurrentScopeSymbol()).irBlock {
|
||||||
context.createIrBuilder(getCurrentScopeSymbol()), expression, safe = expression.origin != UNSAFE_MFVC_SET_ORIGIN
|
valueDeclarationsRemapper.addReplacement(this, expression, safe = expression.origin != UNSAFE_MFVC_SET_ORIGIN)
|
||||||
) ?: expression.also { it.value = it.value.transform(this, null) }
|
?: return expression.also { it.value = it.value.transform(this@JvmMultiFieldValueClassLowering, null) }
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitGetValue(expression: IrGetValue): IrExpression = with(context.createIrBuilder(getCurrentScopeSymbol())) b@{
|
|
||||||
with(valueDeclarationsRemapper) {
|
|
||||||
this.makeReplacement(this@b, expression) ?: super.visitGetValue(expression)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitSetValue(expression: IrSetValue): IrExpression = with(context.createIrBuilder(getCurrentScopeSymbol())) b@{
|
override fun visitGetValue(expression: IrGetValue): IrExpression = with(context.createIrBuilder(getCurrentScopeSymbol())) b@{
|
||||||
with(valueDeclarationsRemapper) {
|
valueDeclarationsRemapper.makeReplacement(this@b, expression) ?: super.visitGetValue(expression)
|
||||||
this.makeReplacement(this@b, expression, safe = expression.origin != UNSAFE_MFVC_SET_ORIGIN)
|
}
|
||||||
} ?: super.visitSetValue(expression)
|
|
||||||
|
override fun visitSetValue(expression: IrSetValue): IrExpression = context.createIrBuilder(getCurrentScopeSymbol()).irBlock b@{
|
||||||
|
valueDeclarationsRemapper.addReplacement(this@b, expression, safe = expression.origin != UNSAFE_MFVC_SET_ORIGIN)
|
||||||
|
?: return super.visitSetValue(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitVariable(declaration: IrVariable): IrStatement {
|
override fun visitVariable(declaration: IrVariable): IrStatement {
|
||||||
@@ -921,7 +972,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
saveVariable = ::variablesSaver
|
saveVariable = ::variablesSaver
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val instance = ValueDeclarationMfvcNodeInstance(this, rootMfvcNode, typeArguments, variables, listOf())
|
val instance = ValueDeclarationMfvcNodeInstance(this, rootMfvcNode, typeArguments, variables)
|
||||||
val block = irBlock {
|
val block = irBlock {
|
||||||
flattenExpressionTo(expression, instance)
|
flattenExpressionTo(expression, instance)
|
||||||
}
|
}
|
||||||
@@ -949,6 +1000,16 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
require(rootNode.leavesCount == instance.size) {
|
require(rootNode.leavesCount == instance.size) {
|
||||||
"Required ${rootNode.leavesCount} variable/field to store regular value but got ${instance.size}"
|
"Required ${rootNode.leavesCount} variable/field to store regular value but got ${instance.size}"
|
||||||
}
|
}
|
||||||
|
if (expression is IrWhen) {
|
||||||
|
for (branch in expression.branches) {
|
||||||
|
branch.condition = branch.condition.transform(this@JvmMultiFieldValueClassLowering, null)
|
||||||
|
branch.result = irBlock {
|
||||||
|
flattenExpressionTo(branch.result, instance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+expression
|
||||||
|
return
|
||||||
|
}
|
||||||
if (expression is IrConstructorCall) {
|
if (expression is IrConstructorCall) {
|
||||||
val constructor = expression.symbol.owner
|
val constructor = expression.symbol.owner
|
||||||
if (constructor.isPrimary && constructor.constructedClass.isMultiFieldValueClass &&
|
if (constructor.isPrimary && constructor.constructedClass.isMultiFieldValueClass &&
|
||||||
@@ -971,24 +1032,15 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val transformedExpression = expression.transform(this@JvmMultiFieldValueClassLowering, null)
|
val transformedExpression = expression.transform(this@JvmMultiFieldValueClassLowering, null)
|
||||||
val flattenedExpressionInstance: MfvcNodeInstance? = valueDeclarationsRemapper.getMfvcNodeInstanceIfIsGetter(transformedExpression)
|
val addedSettersToFlattened = valueDeclarationsRemapper.handleFlattenedGetterExpressions(transformedExpression) {
|
||||||
?.also { flattenedInstance -> flattenedInstance.initializationStatements.forEach { +it } }
|
require(it.size == instance.size) { "Incompatible assignment sizes: ${it.size}, ${instance.size}" }
|
||||||
if (flattenedExpressionInstance == null && transformedExpression is IrContainerExpression && transformedExpression.statements.isNotEmpty()) {
|
instance.addSetterStatements(this, it)
|
||||||
val last = transformedExpression.statements.last()
|
|
||||||
if (last is IrExpression) {
|
|
||||||
fun irContainer(builder: IrBlockBuilder.() -> Unit) =
|
|
||||||
if (transformedExpression.isTransparentScope) irComposite { builder() } else irBlock { builder() }
|
|
||||||
|
|
||||||
+irContainer {
|
|
||||||
for (statement in transformedExpression.statements.dropLast(1)) {
|
|
||||||
+statement
|
|
||||||
}
|
|
||||||
flattenExpressionTo(last, instance)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
val expressionInstance = flattenedExpressionInstance ?: rootNode.createInstanceFromBox(
|
if (addedSettersToFlattened != null) {
|
||||||
|
+addedSettersToFlattened
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val expressionInstance = rootNode.createInstanceFromBox(
|
||||||
this, transformedExpression, getOptimizedPublicAccess(rootNode.mfvc), ::variablesSaver,
|
this, transformedExpression, getOptimizedPublicAccess(rootNode.mfvc), ::variablesSaver,
|
||||||
)
|
)
|
||||||
require(expressionInstance.size == instance.size) { "Incompatible assignment sizes: ${expressionInstance.size}, ${instance.size}" }
|
require(expressionInstance.size == instance.size) { "Incompatible assignment sizes: ${expressionInstance.size}, ${instance.size}" }
|
||||||
@@ -1028,6 +1080,15 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
handleStatementContainer(expression, data)
|
handleStatementContainer(expression, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visitWhen(expression: IrWhen, data: Boolean) {
|
||||||
|
expression.acceptChildren(this, data) // when's are transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitBranch(branch: IrBranch, data: Boolean) {
|
||||||
|
branch.condition.accept(this, true)
|
||||||
|
branch.result.accept(this, data)
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitBlockBody(body: IrBlockBody, data: Boolean) {
|
override fun visitBlockBody(body: IrBlockBody, data: Boolean) {
|
||||||
handleStatementContainer(body, data)
|
handleStatementContainer(body, data)
|
||||||
}
|
}
|
||||||
@@ -1110,9 +1171,9 @@ private fun findNearestBlocksForVariables(variables: Set<IrVariable>, body: IrBo
|
|||||||
private fun IrStatement.containsUsagesOf(variablesSet: Set<IrVariable>): Boolean {
|
private fun IrStatement.containsUsagesOf(variablesSet: Set<IrVariable>): Boolean {
|
||||||
var used = false
|
var used = false
|
||||||
acceptVoid(object : IrElementVisitorVoid {
|
acceptVoid(object : IrElementVisitorVoid {
|
||||||
override fun visitExpression(expression: IrExpression) {
|
override fun visitElement(element: IrElement) {
|
||||||
if (!used) {
|
if (!used) {
|
||||||
expression.acceptChildrenVoid(this)
|
element.acceptChildrenVoid(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.backend.jvm
|
|||||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.isMultiFieldValueClassType
|
import org.jetbrains.kotlin.backend.jvm.ir.isMultiFieldValueClassType
|
||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
|
||||||
import org.jetbrains.kotlin.ir.builders.IrBlockBuilder
|
import org.jetbrains.kotlin.ir.builders.IrBlockBuilder
|
||||||
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
|
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
|
||||||
import org.jetbrains.kotlin.ir.builders.irCall
|
import org.jetbrains.kotlin.ir.builders.irCall
|
||||||
@@ -61,13 +60,13 @@ fun MfvcNode.createInstanceFromValueDeclarations(
|
|||||||
saveVariable = saveVariable
|
saveVariable = saveVariable
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return ValueDeclarationMfvcNodeInstance(scope, this, typeArguments, valueDeclarations, listOf())
|
return ValueDeclarationMfvcNodeInstance(scope, this, typeArguments, valueDeclarations)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MfvcNode.createInstanceFromValueDeclarationsAndBoxType(
|
fun MfvcNode.createInstanceFromValueDeclarationsAndBoxType(
|
||||||
scope: IrBuilderWithScope, type: IrSimpleType, fieldValues: List<IrValueDeclaration>, initializationStatements: List<IrStatement>
|
scope: IrBuilderWithScope, type: IrSimpleType, fieldValues: List<IrValueDeclaration>
|
||||||
): ValueDeclarationMfvcNodeInstance =
|
): ValueDeclarationMfvcNodeInstance =
|
||||||
ValueDeclarationMfvcNodeInstance(scope, this, makeTypeArgumentsFromType(type), fieldValues, initializationStatements)
|
ValueDeclarationMfvcNodeInstance(scope, this, makeTypeArgumentsFromType(type), fieldValues)
|
||||||
|
|
||||||
fun makeTypeArgumentsFromType(type: IrSimpleType): TypeArguments {
|
fun makeTypeArgumentsFromType(type: IrSimpleType): TypeArguments {
|
||||||
if (type.classifierOrNull !is IrClassSymbol) return mapOf()
|
if (type.classifierOrNull !is IrClassSymbol) return mapOf()
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ interface MfvcNodeInstance {
|
|||||||
val node: MfvcNode
|
val node: MfvcNode
|
||||||
val typeArguments: TypeArguments
|
val typeArguments: TypeArguments
|
||||||
val type: IrSimpleType
|
val type: IrSimpleType
|
||||||
val initializationStatements: List<IrStatement>
|
|
||||||
|
|
||||||
fun makeFlattenedGetterExpressions(): List<IrExpression>
|
fun makeFlattenedGetterExpressions(): List<IrExpression>
|
||||||
fun makeGetterExpression(): IrExpression
|
fun makeGetterExpression(): IrExpression
|
||||||
@@ -56,7 +55,6 @@ class ValueDeclarationMfvcNodeInstance(
|
|||||||
override val node: MfvcNode,
|
override val node: MfvcNode,
|
||||||
override val typeArguments: TypeArguments,
|
override val typeArguments: TypeArguments,
|
||||||
val valueDeclarations: List<IrValueDeclaration>,
|
val valueDeclarations: List<IrValueDeclaration>,
|
||||||
override val initializationStatements: List<IrStatement>,
|
|
||||||
) : MfvcNodeInstance {
|
) : MfvcNodeInstance {
|
||||||
init {
|
init {
|
||||||
require(valueDeclarations.size == size) { "Expected value declarations list of size $size but got of size ${valueDeclarations.size}" }
|
require(valueDeclarations.size == size) { "Expected value declarations list of size $size but got of size ${valueDeclarations.size}" }
|
||||||
@@ -73,7 +71,7 @@ class ValueDeclarationMfvcNodeInstance(
|
|||||||
|
|
||||||
override fun get(name: Name): ValueDeclarationMfvcNodeInstance? {
|
override fun get(name: Name): ValueDeclarationMfvcNodeInstance? {
|
||||||
val (newNode, indices) = node.getSubnodeAndIndices(name) ?: return null
|
val (newNode, indices) = node.getSubnodeAndIndices(name) ?: return null
|
||||||
return ValueDeclarationMfvcNodeInstance(scope, newNode, typeArguments, valueDeclarations.slice(indices), initializationStatements)
|
return ValueDeclarationMfvcNodeInstance(scope, newNode, typeArguments, valueDeclarations.slice(indices))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun makeStatements(values: List<IrExpression>): List<IrStatement> {
|
override fun makeStatements(values: List<IrExpression>): List<IrStatement> {
|
||||||
@@ -86,7 +84,6 @@ internal class ExpressionCopierImpl(
|
|||||||
expression: IrExpression?,
|
expression: IrExpression?,
|
||||||
private val scope: IrBlockBuilder,
|
private val scope: IrBlockBuilder,
|
||||||
private val saveVariable: (IrVariable) -> Unit,
|
private val saveVariable: (IrVariable) -> Unit,
|
||||||
private val saveReceiverSetter: (IrSetValue) -> Unit
|
|
||||||
) {
|
) {
|
||||||
private sealed interface CopyableExpression {
|
private sealed interface CopyableExpression {
|
||||||
fun makeExpression(scope: IrBuilderWithScope): IrExpression
|
fun makeExpression(scope: IrBuilderWithScope): IrExpression
|
||||||
@@ -109,7 +106,6 @@ internal class ExpressionCopierImpl(
|
|||||||
origin = IrDeclarationOrigin.TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE,
|
origin = IrDeclarationOrigin.TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE,
|
||||||
saveVariable = saveVariable,
|
saveVariable = saveVariable,
|
||||||
isTemporary = true,
|
isTemporary = true,
|
||||||
saveSetter = saveReceiverSetter,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -156,10 +152,7 @@ class ReceiverBasedMfvcNodeInstance(
|
|||||||
) : MfvcNodeInstance {
|
) : MfvcNodeInstance {
|
||||||
override val type: IrSimpleType = makeTypeFromMfvcNodeAndTypeArguments(node, typeArguments)
|
override val type: IrSimpleType = makeTypeFromMfvcNodeAndTypeArguments(node, typeArguments)
|
||||||
|
|
||||||
override val initializationStatements: List<IrStatement> = mutableListOf()
|
private val receiverCopier = ExpressionCopierImpl(receiver, scope, saveVariable)
|
||||||
private val receiverCopier = ExpressionCopierImpl(
|
|
||||||
receiver, scope, saveVariable, saveReceiverSetter = { (initializationStatements as MutableList).add(it) }
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun makeReceiverCopy() = receiverCopier.makeCopy()
|
private fun makeReceiverCopy() = receiverCopier.makeCopy()
|
||||||
|
|
||||||
@@ -280,8 +273,7 @@ fun <T : IrElement> IrStatementsBuilder<T>.savableStandaloneVariableWithSetter(
|
|||||||
isMutable: Boolean = false,
|
isMutable: Boolean = false,
|
||||||
origin: IrDeclarationOrigin,
|
origin: IrDeclarationOrigin,
|
||||||
isTemporary: Boolean = origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
|
isTemporary: Boolean = origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
|
||||||
saveSetter: (IrSetValue) -> Unit = {},
|
|
||||||
saveVariable: (IrVariable) -> Unit,
|
saveVariable: (IrVariable) -> Unit,
|
||||||
) = savableStandaloneVariable(expression.type, name, isMutable, origin, isTemporary, saveVariable).also {
|
) = savableStandaloneVariable(expression.type, name, isMutable, origin, isTemporary, saveVariable).also {
|
||||||
+irSet(it, expression).also(saveSetter)
|
+irSet(it, expression)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// CHECK_BYTECODE_LISTING
|
||||||
|
// WITH_STDLIB
|
||||||
|
// TARGET_BACKEND: JVM_IR
|
||||||
|
// WORKS_WHEN_VALUE_CLASS
|
||||||
|
// LANGUAGE: +ValueClasses
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class DPoint(val x: Double, val y: Double) {
|
||||||
|
init {
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
var counter: Int = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ifExpr() = if (DPoint(0.0, 1.0).x > 0.0) DPoint(2.0, 3.0) else DPoint(4.0, 5.0)
|
||||||
|
|
||||||
|
fun whenExpr() = when {
|
||||||
|
DPoint(6.0, 7.0).x > 0.0 -> DPoint(8.0, 9.0)
|
||||||
|
DPoint(10.0, 11.0).x > 0.0 -> DPoint(12.0, 13.0)
|
||||||
|
else -> DPoint(14.0, 15.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ifBody() {
|
||||||
|
if (DPoint(0.0, 1.0).x > 0.0) DPoint(2.0, 3.0) else DPoint(4.0, 5.0)
|
||||||
|
val x = if (DPoint(0.0, 1.0).x > 0.0) DPoint(2.0, 3.0) else DPoint(4.0, 5.0)
|
||||||
|
require(x == DPoint(4.0, 5.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun whenBody() {
|
||||||
|
when {
|
||||||
|
DPoint(6.0, 7.0).x > 0.0 -> DPoint(8.0, 9.0)
|
||||||
|
DPoint(10.0, 11.0).x > 0.0 -> DPoint(12.0, 13.0)
|
||||||
|
else -> DPoint(14.0, 15.0)
|
||||||
|
}
|
||||||
|
val x = when {
|
||||||
|
DPoint(6.0, 7.0).x > 0.0 -> DPoint(8.0, 9.0)
|
||||||
|
DPoint(10.0, 11.0).x > 0.0 -> DPoint(12.0, 13.0)
|
||||||
|
else -> DPoint(14.0, 15.0)
|
||||||
|
}
|
||||||
|
require(x == DPoint(8.0, 9.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
ifBody()
|
||||||
|
whenBody()
|
||||||
|
require(ifExpr() == DPoint(4.0, 5.0))
|
||||||
|
require(whenExpr() == DPoint(8.0, 9.0))
|
||||||
|
require(DPoint.counter == 5 + 5 + 2 + 1 + 2 + 1)
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
@kotlin.Metadata
|
||||||
|
public final class ConditionalExpressionsKt {
|
||||||
|
// source: 'conditionalExpressions.kt'
|
||||||
|
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||||
|
public final static method ifBody(): void
|
||||||
|
public final static @org.jetbrains.annotations.NotNull method ifExpr(): DPoint
|
||||||
|
public final static method whenBody(): void
|
||||||
|
public final static @org.jetbrains.annotations.NotNull method whenExpr(): DPoint
|
||||||
|
public final inner class DPoint$Companion
|
||||||
|
}
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
public final class DPoint$Companion {
|
||||||
|
// source: 'conditionalExpressions.kt'
|
||||||
|
private method <init>(): void
|
||||||
|
public synthetic method <init>(p0: kotlin.jvm.internal.DefaultConstructorMarker): void
|
||||||
|
public synthetic deprecated static @kotlin.jvm.JvmStatic method getCounter$annotations(): void
|
||||||
|
public final method getCounter(): int
|
||||||
|
public final method setCounter(p0: int): void
|
||||||
|
public final inner class DPoint$Companion
|
||||||
|
}
|
||||||
|
|
||||||
|
@kotlin.jvm.JvmInline
|
||||||
|
@kotlin.Metadata
|
||||||
|
public final class DPoint {
|
||||||
|
// source: 'conditionalExpressions.kt'
|
||||||
|
public final static @org.jetbrains.annotations.NotNull field Companion: DPoint$Companion
|
||||||
|
private static field counter: int
|
||||||
|
private final field field-0: double
|
||||||
|
private final field field-1: double
|
||||||
|
static method <clinit>(): void
|
||||||
|
private synthetic method <init>(p0: double, p1: double): void
|
||||||
|
public synthetic final static method access$getCounter$cp(): int
|
||||||
|
public synthetic final static method access$setCounter$cp(p0: int): void
|
||||||
|
public synthetic final static method box-impl(p0: double, p1: double): DPoint
|
||||||
|
public final static method constructor-impl(p0: double, p1: double): void
|
||||||
|
public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean
|
||||||
|
public static method equals-impl(p0: double, p1: double, p2: java.lang.Object): boolean
|
||||||
|
public final static method equals-impl0(p0: double, p1: double, p2: double, p3: double): boolean
|
||||||
|
public final static method getCounter(): int
|
||||||
|
public final static method getX-impl(p0: double, p1: double): double
|
||||||
|
public final static method getY-impl(p0: double, p1: double): double
|
||||||
|
public method hashCode(): int
|
||||||
|
public static method hashCode-impl(p0: double, p1: double): int
|
||||||
|
public final static method setCounter(p0: int): void
|
||||||
|
public @org.jetbrains.annotations.NotNull method toString(): java.lang.String
|
||||||
|
public static method toString-impl(p0: double, p1: double): java.lang.String
|
||||||
|
public synthetic final method unbox-impl-0(): double
|
||||||
|
public synthetic final method unbox-impl-1(): double
|
||||||
|
public final inner class DPoint$Companion
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ public final class Internal {
|
|||||||
public final static @org.jetbrains.annotations.NotNull field Companion: Internal$Companion
|
public final static @org.jetbrains.annotations.NotNull field Companion: Internal$Companion
|
||||||
private final field field-0: int
|
private final field field-0: int
|
||||||
private final field field-1: int
|
private final field field-1: int
|
||||||
|
private static field x: int
|
||||||
static method <clinit>(): void
|
static method <clinit>(): void
|
||||||
private synthetic method <init>(p0: int, p1: int): void
|
private synthetic method <init>(p0: int, p1: int): void
|
||||||
public synthetic final static method access$getX$cp(): int
|
public synthetic final static method access$getX$cp(): int
|
||||||
@@ -228,6 +229,7 @@ public final class Public {
|
|||||||
public final static @org.jetbrains.annotations.NotNull field Companion: Public$Companion
|
public final static @org.jetbrains.annotations.NotNull field Companion: Public$Companion
|
||||||
private final field field-0: int
|
private final field field-0: int
|
||||||
private final field field-1: int
|
private final field field-1: int
|
||||||
|
private static field x: int
|
||||||
static method <clinit>(): void
|
static method <clinit>(): void
|
||||||
private synthetic method <init>(p0: int, p1: int): void
|
private synthetic method <init>(p0: int, p1: int): void
|
||||||
public synthetic final static method access$getX$cp(): int
|
public synthetic final static method access$getX$cp(): int
|
||||||
|
|||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
// CHECK_BYTECODE_LISTING
|
||||||
|
// WITH_STDLIB
|
||||||
|
// TARGET_BACKEND: JVM_IR
|
||||||
|
// WORKS_WHEN_VALUE_CLASS
|
||||||
|
// LANGUAGE: +ValueClasses
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class DPoint(val x: Double, val y: Double)
|
||||||
|
|
||||||
|
fun ifExpr() = if (DPoint(0.0, 1.0).x > 0.0) DPoint(2.0, 3.0) else DPoint(4.0, 5.0)
|
||||||
|
|
||||||
|
fun whenExpr() = when {
|
||||||
|
DPoint(6.0, 7.0).x > 0.0 -> DPoint(8.0, 9.0)
|
||||||
|
DPoint(10.0, 11.0).x > 0.0 -> DPoint(12.0, 13.0)
|
||||||
|
else -> DPoint(14.0, 15.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ifBody() {
|
||||||
|
if (DPoint(0.0, 1.0).x > 0.0) DPoint(2.0, 3.0) else DPoint(4.0, 5.0)
|
||||||
|
val x = if (DPoint(0.0, 1.0).x > 0.0) DPoint(2.0, 3.0) else DPoint(4.0, 5.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun whenBody() {
|
||||||
|
when {
|
||||||
|
DPoint(6.0, 7.0).x > 0.0 -> DPoint(8.0, 9.0)
|
||||||
|
DPoint(10.0, 11.0).x > 0.0 -> DPoint(12.0, 13.0)
|
||||||
|
else -> DPoint(14.0, 15.0)
|
||||||
|
}
|
||||||
|
val x = when {
|
||||||
|
DPoint(6.0, 7.0).x > 0.0 -> DPoint(8.0, 9.0)
|
||||||
|
DPoint(10.0, 11.0).x > 0.0 -> DPoint(12.0, 13.0)
|
||||||
|
else -> DPoint(14.0, 15.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 ifExpr.*(\n .*)(\n .*)*(\n .*box-impl.*)(\n .*)*(\n .*box-impl.*)
|
||||||
|
// 0 ifExpr.*(\n .*)(\n .*)*(\n .*box-impl.*)(\n .*)*(\n .*box-impl.*)(\n .*)*(\n .*box-impl.*)
|
||||||
|
// 1 whenExpr.*(\n .*)(\n .*)*(\n .*box-impl.*)(\n .*)*(\n .*box-impl.*)(\n .*)*(\n .*box-impl.*)
|
||||||
|
// 0 whenExpr.*(\n .*)(\n .*)*(\n .*box-impl.*)(\n .*)*(\n .*box-impl.*)(\n .*)*(\n .*box-impl.*)(\n .*)*(\n .*box-impl.*)
|
||||||
|
// 0 ifBody.*(\n .*)*(\n .*box-impl.*)
|
||||||
|
// 0 whenBody.*(\n .*)*(\n .*box-impl.*)
|
||||||
+6
@@ -50195,6 +50195,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/valueClasses/complex.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
runTest("compiler/testData/codegen/box/valueClasses/complex.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("conditionalExpressions.kt")
|
||||||
|
public void testConditionalExpressions() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/valueClasses/conditionalExpressions.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("equality.kt")
|
@TestMetadata("equality.kt")
|
||||||
public void testEquality() throws Exception {
|
public void testEquality() throws Exception {
|
||||||
|
|||||||
+6
@@ -5789,6 +5789,12 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("conditionalExpressions.kt")
|
||||||
|
public void testConditionalExpressions() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/valueClasses/conditionalExpressions.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("equalsBoxTest.kt")
|
@TestMetadata("equalsBoxTest.kt")
|
||||||
public void testEqualsBoxTest() throws Exception {
|
public void testEqualsBoxTest() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user