[IR] Generify lowerings

#KT-1179
This commit is contained in:
Evgeniy.Zhelenskiy
2022-03-06 03:22:35 +03:00
committed by teamcity
parent 22b2554368
commit 282ab398c6
36 changed files with 3972 additions and 373 deletions
@@ -9,10 +9,7 @@ import com.intellij.psi.PsiCompiledElement
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.InlineClassRepresentation
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.backend.generators.FakeOverrideGenerator
import org.jetbrains.kotlin.fir.builder.buildPackageDirective
@@ -71,6 +68,7 @@ import org.jetbrains.kotlin.resolve.calls.util.isSingleUnderscore
import org.jetbrains.kotlin.types.ConstantValueKind
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal fun <T : IrElement> FirElement.convertWithOffsets(
f: (startOffset: Int, endOffset: Int) -> T
@@ -196,7 +194,15 @@ fun FirReference.toSymbolForCall(
): IrSymbol? {
return when (this) {
is FirResolvedNamedReference ->
resolvedSymbol.toSymbolForCall(dispatchReceiver, session, classifierStorage, declarationStorage, preferGetter, explicitReceiver, isDelegate)
resolvedSymbol.toSymbolForCall(
dispatchReceiver,
session,
classifierStorage,
declarationStorage,
preferGetter,
explicitReceiver,
isDelegate
)
is FirErrorNamedReference ->
candidateSymbol?.toSymbolForCall(
dispatchReceiver,
@@ -648,6 +654,16 @@ fun Fir2IrComponents.computeInlineClassRepresentation(klass: FirRegularClass): I
)
}
// TODO: implement multiFieldValueClassRepresentation in FirRegularClass instead.
fun Fir2IrComponents.computeMultiFieldValueClassRepresentation(klass: FirRegularClass): MultiFieldValueClassRepresentation<IrSimpleType>? {
val parameters = klass.getMultiFieldValueClassUnderlyingParameters(session) ?: return null
return MultiFieldValueClassRepresentation(parameters.map {
val type = it.returnTypeRef.toIrType(typeConverter).safeAs<IrSimpleType>()
?: error("Value class underlying type is not a simple type: ${klass.render()}")
it.name to type
})
}
fun FirRegularClass.getIrSymbolsForSealedSubclasses(components: Fir2IrComponents): List<IrClassSymbol> {
val session = components.session
val symbolProvider = session.symbolProvider
@@ -139,6 +139,11 @@ class Fir2IrLazyClass(
set(_) {
error("Mutating Fir2Ir lazy elements is not possible")
}
override var multiFieldValueClassRepresentation: MultiFieldValueClassRepresentation<IrSimpleType>?
get() = computeMultiFieldValueClassRepresentation(fir)
set(_) {
error("Mutating Fir2Ir lazy elements is not possible")
}
private val fakeOverridesByName = mutableMapOf<Name, Collection<IrDeclaration>>()
@@ -48207,6 +48207,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("classFlattening.kt")
public void testClassFlattening() throws Exception {
runTest("compiler/testData/codegen/box/valueClasses/classFlattening.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
}
@Test
@TestMetadata("equality.kt")
public void testEquality() throws Exception {
@@ -38,3 +38,6 @@ internal fun ConeKotlinType.unsubstitutedUnderlyingTypeForInlineClass(session: F
// TODO: implement inlineClassRepresentation in FirRegularClass instead.
fun FirRegularClass.getInlineClassUnderlyingParameter(session: FirSession): FirValueParameter? =
if (isInline) primaryConstructorIfAny(session)?.fir?.valueParameters?.singleOrNull() else null
fun FirRegularClass.getMultiFieldValueClassUnderlyingParameters(session: FirSession): List<FirValueParameter>? =
if (isInline) primaryConstructorIfAny(session)?.fir?.valueParameters?.takeIf { it.size > 1 } else null
@@ -95,6 +95,7 @@ class SyntheticClassOrObjectDescriptor(
override fun getUnsubstitutedMemberScope(kotlinTypeRefiner: KotlinTypeRefiner) = unsubstitutedMemberScope
override fun getSealedSubclasses() = emptyList<ClassDescriptor>()
override fun getInlineClassRepresentation(): InlineClassRepresentation<SimpleType>? = null
override fun getMultiFieldValueClassRepresentation(): MultiFieldValueClassRepresentation<SimpleType>? = null
init {
assert(modality != Modality.SEALED) { "Implement getSealedSubclasses() for this class: ${this::class.java}" }
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.lazy.descriptors;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNameIdentifierOwner;
import kotlin.Pair;
import kotlin.annotations.jvm.ReadOnly;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function1;
@@ -48,6 +49,7 @@ import org.jetbrains.kotlin.storage.NullableLazyValue;
import org.jetbrains.kotlin.storage.StorageManager;
import org.jetbrains.kotlin.types.*;
import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner;
import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext;
import java.util.*;
import java.util.stream.Collectors;
@@ -646,6 +648,28 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes
);
}
@Nullable
@Override
public MultiFieldValueClassRepresentation<SimpleType> getMultiFieldValueClassRepresentation() {
if (!InlineClassesUtilsKt.isValueClass(this) || InlineClassesUtilsKt.isInlineClass(this)) {
return null;
}
ClassConstructorDescriptor constructor = getUnsubstitutedPrimaryConstructor();
// Don't crash on invalid code. It is IC, not MFVC.
if (constructor == null) {
return null;
}
List<ValueParameterDescriptor> parameters = constructor.getValueParameters();
if (parameters.size() <= 1) {
return null;
}
List<Pair<Name, SimpleType>> properties = parameters.stream()
.map(parameter -> new Pair<>(parameter.getName(), (SimpleType) parameter.getType()))
.collect(Collectors.toList());
return new MultiFieldValueClassRepresentation<>(properties);
}
@Override
public String toString() {
// not using DescriptorRenderer to preserve laziness
@@ -5,8 +5,6 @@
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.copyParameterDeclarationsFrom
import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
@@ -28,15 +26,16 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.transformStatement
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.makeNotNull
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.JVM_INLINE_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
val jvmInlineClassPhase = makeIrFilePhase(
::JvmInlineClassLowering,
@@ -57,96 +56,24 @@ val jvmInlineClassPhase = makeIrFilePhase(
* We do not unfold inline class types here. Instead, the type mapper will lower inline class
* types to the types of their underlying field.
*/
private class JvmInlineClassLowering(private val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() {
private val valueMap = mutableMapOf<IrValueSymbol, IrValueDeclaration>()
private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClassAbstractLowering(context) {
override val replacements: MemoizedValueClassAbstractReplacements
get() = context.inlineClassReplacements
private fun addBindingsFor(original: IrFunction, replacement: IrFunction) {
for ((param, newParam) in original.explicitParameters.zip(replacement.explicitParameters)) {
valueMap[param.symbol] = newParam
}
}
override fun IrClass.isSpecificLoweringLogicApplicable(): Boolean = isSingleFieldValueClass
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid()
}
override fun IrFunction.isSpecificFieldGetter(): Boolean = isInlineClassFieldGetter
override fun visitClassNew(declaration: IrClass): IrStatement {
// The arguments to the primary constructor are in scope in the initializers of IrFields.
declaration.primaryConstructor?.let {
context.inlineClassReplacements.getReplacementFunction(it)?.let { replacement -> addBindingsFor(it, replacement) }
}
declaration.transformDeclarationsFlat { memberDeclaration ->
if (memberDeclaration is IrFunction) {
withinScope(memberDeclaration) {
transformFunctionFlat(memberDeclaration)
}
} else {
memberDeclaration.accept(this, null)
null
}
}
if (declaration.isSingleFieldValueClass) {
val irConstructor = declaration.primaryConstructor!!
// The field getter is used by reflection and cannot be removed here unless it is internal.
declaration.declarations.removeIf {
it == irConstructor || (it is IrFunction && it.isInlineClassFieldGetter && !it.visibility.isPublicAPI)
}
buildPrimaryInlineClassConstructor(declaration, irConstructor)
buildBoxFunction(declaration)
buildUnboxFunction(declaration)
buildSpecializedEqualsMethod(declaration)
addJvmInlineAnnotation(declaration)
}
return declaration
}
private fun addJvmInlineAnnotation(declaration: IrClass) {
if (declaration.hasAnnotation(JVM_INLINE_ANNOTATION_FQ_NAME)) return
override fun addJvmInlineAnnotation(valueClass: IrClass) {
if (valueClass.hasAnnotation(JVM_INLINE_ANNOTATION_FQ_NAME)) return
val constructor = context.ir.symbols.jvmInlineAnnotation.constructors.first()
declaration.annotations = declaration.annotations + IrConstructorCallImpl.fromSymbolOwner(
valueClass.annotations = valueClass.annotations + IrConstructorCallImpl.fromSymbolOwner(
constructor.owner.returnType,
constructor
)
}
private fun transformFunctionFlat(function: IrFunction): List<IrDeclaration>? {
if (function is IrConstructor && function.isPrimary && function.constructedClass.isSingleFieldValueClass)
return null
val replacement = context.inlineClassReplacements.getReplacementFunction(function)
if (replacement == null) {
function.transformChildrenVoid()
return null
}
if (function is IrSimpleFunction && function.overriddenSymbols.any { it.owner.parentAsClass.isFun }) {
// If fun interface methods are already mangled, do not mangle them twice.
val suffix = function.hashSuffix()
if (suffix != null && function.name.asString().endsWith(suffix)) {
function.transformChildrenVoid()
return null
}
}
addBindingsFor(function, replacement)
return when (function) {
is IrSimpleFunction -> transformSimpleFunctionFlat(function, replacement)
is IrConstructor -> transformConstructorFlat(function, replacement)
else -> throw IllegalStateException()
}
}
private fun IrFunction.hashSuffix(): String? =
InlineClassAbi.hashSuffix(
this,
context.state.functionsWithInlineClassReturnTypesMangled,
context.state.useOldManglingSchemeForFunctionsWithInlineClassesInSignatures
)
private fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
override fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
replacement.valueParameters.forEach {
it.transformChildrenVoid()
it.defaultValue?.patchDeclarationParents(replacement)
@@ -182,11 +109,11 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
bridgeFunction.overriddenSymbols = replacement.overriddenSymbols
// Replace the function body with a wrapper
if (!bridgeFunction.isFakeOverride || !bridgeFunction.parentAsClass.isSingleFieldValueClass) {
createBridgeBody(bridgeFunction, replacement)
} else {
if (bridgeFunction.isFakeOverride && bridgeFunction.parentAsClass.isSingleFieldValueClass) {
// Fake overrides redirect from the replacement to the original function, which is in turn replaced during interfacePhase.
createBridgeBody(replacement, bridgeFunction)
} else {
createBridgeBody(bridgeFunction, replacement)
}
return listOf(replacement, bridgeFunction)
@@ -227,7 +154,8 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
// Secondary constructors for boxed types get translated to static functions returning
// unboxed arguments. We remove the original constructor.
private fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
// Primary constructors' case is handled at the start of transformFunctionFlat
override fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
replacement.valueParameters.forEach { it.transformChildrenVoid() }
replacement.body = context.createIrBuilder(replacement.symbol, replacement.startOffset, replacement.endOffset).irBlockBody(
replacement
@@ -469,40 +397,6 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
return super.visitGetField(expression)
}
override fun visitReturn(expression: IrReturn): IrExpression {
expression.returnTargetSymbol.owner.safeAs<IrFunction>()?.let { target ->
val suffix = target.hashSuffix()
if (suffix != null && target.name.asString().endsWith(suffix))
return super.visitReturn(expression)
context.inlineClassReplacements.getReplacementFunction(target)?.let {
return context.createIrBuilder(it.symbol, expression.startOffset, expression.endOffset).irReturn(
expression.value.transform(this, null)
)
}
}
return super.visitReturn(expression)
}
private fun visitStatementContainer(container: IrStatementContainer) {
container.statements.transformFlat { statement ->
if (statement is IrFunction)
transformFunctionFlat(statement)
else
listOf(statement.transformStatement(this))
}
}
override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
visitStatementContainer(expression)
return expression
}
override fun visitBlockBody(body: IrBlockBody): IrBody {
visitStatementContainer(body)
return body
}
override fun visitGetValue(expression: IrGetValue): IrExpression {
valueMap[expression.symbol]?.let {
return IrGetValueImpl(
@@ -525,16 +419,9 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
return super.visitSetValue(expression)
}
// Anonymous initializers in inline classes are processed when building the primary constructor.
override fun visitAnonymousInitializerNew(declaration: IrAnonymousInitializer): IrStatement {
if (declaration.parent.safeAs<IrClass>()?.isSingleFieldValueClass == true)
return declaration
return super.visitAnonymousInitializerNew(declaration)
}
private fun buildPrimaryInlineClassConstructor(irClass: IrClass, irConstructor: IrConstructor) {
override fun buildPrimaryValueClassConstructor(valueClass: IrClass, irConstructor: IrConstructor) {
// Add the default primary constructor
irClass.addConstructor {
valueClass.addConstructor {
updateFrom(irConstructor)
visibility = DescriptorVisibilities.PRIVATE
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER
@@ -547,8 +434,8 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
body = context.createIrBuilder(this.symbol).irBlockBody(this) {
+irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single())
+irSetField(
irGet(irClass.thisReceiver!!),
getInlineClassBackingField(irClass),
irGet(valueClass.thisReceiver!!),
getInlineClassBackingField(valueClass),
irGet(this@apply.valueParameters[0])
)
}
@@ -558,13 +445,13 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
// null-checks, default arguments, and anonymous initializers.
val function = context.inlineClassReplacements.getReplacementFunction(irConstructor)!!
val initBlocks = irClass.declarations.filterIsInstance<IrAnonymousInitializer>()
val initBlocks = valueClass.declarations.filterIsInstance<IrAnonymousInitializer>()
function.valueParameters.forEach { it.transformChildrenVoid() }
function.body = context.createIrBuilder(function.symbol).irBlockBody {
val argument = function.valueParameters[0]
val thisValue = irTemporary(coerceInlineClasses(irGet(argument), argument.type, function.returnType, skipCast = true))
valueMap[irClass.thisReceiver!!.symbol] = thisValue
valueMap[valueClass.thisReceiver!!.symbol] = thisValue
for (initBlock in initBlocks) {
for (stmt in initBlock.body.statements) {
+stmt.transformStatement(this@JvmInlineClassLowering).patchDeclarationParents(function)
@@ -573,21 +460,25 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
+irReturn(irGet(thisValue))
}
irClass.declarations.removeAll(initBlocks)
irClass.declarations += function
valueClass.declarations.removeAll(initBlocks)
valueClass.declarations += function
}
private fun buildBoxFunction(irClass: IrClass) {
val function = context.inlineClassReplacements.getBoxFunction(irClass)
override fun buildBoxFunction(valueClass: IrClass) {
val function = context.inlineClassReplacements.getBoxFunction(valueClass)
with(context.createIrBuilder(function.symbol)) {
function.body = irExprBody(
irCall(irClass.primaryConstructor!!.symbol).apply {
irCall(valueClass.primaryConstructor!!.symbol).apply {
passTypeArgumentsFrom(function)
putValueArgument(0, irGet(function.valueParameters[0]))
}
)
}
irClass.declarations += function
valueClass.declarations += function
}
override fun buildUnboxFunctions(valueClass: IrClass) {
buildUnboxFunction(valueClass)
}
private fun buildUnboxFunction(irClass: IrClass) {
@@ -602,13 +493,13 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
irClass.declarations += function
}
private fun buildSpecializedEqualsMethod(irClass: IrClass) {
val function = context.inlineClassReplacements.getSpecializedEqualsMethod(irClass, context.irBuiltIns)
override fun buildSpecializedEqualsMethod(valueClass: IrClass) {
val function = context.inlineClassReplacements.getSpecializedEqualsMethod(valueClass, context.irBuiltIns)
val left = function.valueParameters[0]
val right = function.valueParameters[1]
val type = left.type.unboxInlineClass()
function.body = context.createIrBuilder(irClass.symbol).run {
function.body = context.createIrBuilder(valueClass.symbol).run {
irExprBody(
irEquals(
coerceInlineClasses(irGet(left), left.type, type),
@@ -617,6 +508,6 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
)
}
irClass.declarations += function
valueClass.declarations += function
}
}
@@ -0,0 +1,78 @@
/*
* Copyright 2010-2022 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.backend.jvm.lower
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassAbstractReplacements
import org.jetbrains.kotlin.backend.jvm.isMultiFieldValueClassFieldGetter
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
private class JvmInlineMultiFieldValueClassLowering(context: JvmBackendContext) : JvmValueClassAbstractLowering(context) {
override val replacements: MemoizedValueClassAbstractReplacements
get() = context.multiFieldValueClassReplacements
override fun IrClass.isSpecificLoweringLogicApplicable(): Boolean = isMultiFieldValueClass
override fun IrFunction.isSpecificFieldGetter(): Boolean = isMultiFieldValueClassFieldGetter
override fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
TODO()
}
override fun buildPrimaryValueClassConstructor(valueClass: IrClass, irConstructor: IrConstructor) {
TODO("Not yet implemented")
}
override fun buildBoxFunction(valueClass: IrClass) {
TODO("Not yet implemented")
}
override fun buildUnboxFunctions(valueClass: IrClass) {
TODO("Not yet implemented")
}
override fun buildSpecializedEqualsMethod(valueClass: IrClass) {
TODO("Not yet implemented")
}
override fun addJvmInlineAnnotation(valueClass: IrClass) = Unit
@Suppress("UNUSED_PARAMETER")
override fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
TODO()
}
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
// todo implement
return super.visitFunctionReference(expression)
}
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
// todo implement
return super.visitFunctionAccess(expression)
}
override fun visitCall(expression: IrCall): IrExpression {
// todo implement
return super.visitCall(expression)
}
override fun visitGetField(expression: IrGetField): IrExpression {
// todo implement
return super.visitGetField(expression)
}
override fun visitGetValue(expression: IrGetValue): IrExpression {
// todo implement
return super.visitGetValue(expression)
}
override fun visitSetValue(expression: IrSetValue): IrExpression {
// todo implement
return super.visitSetValue(expression)
}
}
@@ -0,0 +1,173 @@
/*
* Copyright 2010-2022 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.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.jvm.InlineClassAbi
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassAbstractReplacements
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.transformStatement
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendContext) : FileLoweringPass,
IrElementTransformerVoidWithContext() {
abstract val replacements: MemoizedValueClassAbstractReplacements
protected val valueMap = mutableMapOf<IrValueSymbol, IrValueDeclaration>()
private fun addBindingsFor(original: IrFunction, replacement: IrFunction) {
for ((param, newParam) in original.explicitParameters.zip(replacement.explicitParameters)) {
valueMap[param.symbol] = newParam
}
}
final override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid()
}
abstract fun IrClass.isSpecificLoweringLogicApplicable(): Boolean
abstract fun IrFunction.isSpecificFieldGetter(): Boolean
final override fun visitClassNew(declaration: IrClass): IrStatement {
// The arguments to the primary constructor are in scope in the initializers of IrFields.
declaration.primaryConstructor?.let {
replacements.getReplacementFunction(it)?.let { replacement -> addBindingsFor(it, replacement) }
}
declaration.transformDeclarationsFlat { memberDeclaration ->
if (memberDeclaration is IrFunction) {
withinScope(memberDeclaration) {
transformFunctionFlat(memberDeclaration)
}
} else {
memberDeclaration.accept(this, null)
null
}
}
if (declaration.isSpecificLoweringLogicApplicable()) {
val irConstructor = declaration.primaryConstructor!!
// The field getter is used by reflection and cannot be removed here unless it is internal.
declaration.declarations.removeIf {
it == irConstructor || (it is IrFunction && it.isSpecificFieldGetter() && !it.visibility.isPublicAPI)
}
buildPrimaryValueClassConstructor(declaration, irConstructor)
buildBoxFunction(declaration)
buildUnboxFunctions(declaration)
buildSpecializedEqualsMethod(declaration)
addJvmInlineAnnotation(declaration)
}
return declaration
}
protected fun transformFunctionFlat(function: IrFunction): List<IrDeclaration>? {
if (function is IrConstructor && function.isPrimary && function.constructedClass.isSpecificLoweringLogicApplicable()) {
return null
}
val replacement = replacements.getReplacementFunction(function)
if (replacement == null) {
function.transformChildrenVoid()
return null
}
if (function is IrSimpleFunction && function.overriddenSymbols.any { it.owner.parentAsClass.isFun }) {
// If fun interface methods are already mangled, do not mangle them twice.
val suffix = function.hashSuffix()
if (suffix != null && function.name.asString().endsWith(suffix)) {
function.transformChildrenVoid()
return null
}
}
addBindingsFor(function, replacement)
return when (function) {
is IrSimpleFunction -> transformSimpleFunctionFlat(function, replacement)
is IrConstructor -> transformConstructorFlat(function, replacement)
else -> throw IllegalStateException()
}
}
private fun IrFunction.hashSuffix(): String? = InlineClassAbi.hashSuffix(
this,
context.state.functionsWithInlineClassReturnTypesMangled,
context.state.useOldManglingSchemeForFunctionsWithInlineClassesInSignatures
)
protected abstract fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration>
protected abstract fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration>
protected abstract fun buildPrimaryValueClassConstructor(valueClass: IrClass, irConstructor: IrConstructor)
protected abstract fun buildBoxFunction(valueClass: IrClass)
protected abstract fun buildUnboxFunctions(valueClass: IrClass)
protected abstract fun buildSpecializedEqualsMethod(valueClass: IrClass) // todo hashCode
protected abstract fun addJvmInlineAnnotation(valueClass: IrClass)
// abstract override fun visitFunctionReference(expression: IrFunctionReference): IrExpression
// abstract override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression
// abstract override fun visitCall(expression: IrCall): IrExpression
// abstract override fun visitGetField(expression: IrGetField): IrExpression
final override fun visitReturn(expression: IrReturn): IrExpression {
expression.returnTargetSymbol.owner.safeAs<IrFunction>()?.let { target ->
val suffix = target.hashSuffix()
if (suffix != null && target.name.asString().endsWith(suffix))
return super.visitReturn(expression)
replacements.getReplacementFunction(target)?.let {
return context.createIrBuilder(it.symbol, expression.startOffset, expression.endOffset).irReturn(
expression.value.transform(this, null)
)
}
}
return super.visitReturn(expression)
}
private fun visitStatementContainer(container: IrStatementContainer) {
container.statements.transformFlat { statement ->
if (statement is IrFunction)
transformFunctionFlat(statement)
else
listOf(statement.transformStatement(this))
}
}
final override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
visitStatementContainer(expression)
return expression
}
final override fun visitBlockBody(body: IrBlockBody): IrBody {
visitStatementContainer(body)
return body
}
// Anonymous initializers in inline classes are processed when building the primary constructor.
final override fun visitAnonymousInitializerNew(declaration: IrAnonymousInitializer): IrStatement =
if (declaration.parent.safeAs<IrClass>()?.isSpecificLoweringLogicApplicable() == true)
declaration
else
super.visitAnonymousInitializerNew(declaration)
}
@@ -147,3 +147,11 @@ val IrClass.inlineClassFieldName: Name
val IrFunction.isInlineClassFieldGetter: Boolean
get() = (parent as? IrClass)?.isSingleFieldValueClass == true && this is IrSimpleFunction && extensionReceiverParameter == null &&
correspondingPropertySymbol?.let { it.owner.getter == this && it.owner.name == parentAsClass.inlineClassFieldName } == true
val IrFunction.isMultiFieldValueClassFieldGetter: Boolean
get() = (parent as? IrClass)?.isMultiFieldValueClass == true && this is IrSimpleFunction && extensionReceiverParameter == null &&
correspondingPropertySymbol?.let {
val multiFieldValueClassRepresentation = parentAsClass.multiFieldValueClassRepresentation
?: error("Multi-field value class must have multiFieldValueClassRepresentation: ${parentAsClass.render()}")
it.owner.getter == this && multiFieldValueClassRepresentation.containsPropertyWithName(it.owner.name)
} == true
@@ -129,6 +129,9 @@ class JvmBackendContext(
val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory, this)
val multiFieldValueClassReplacements =
MemoizedMultiFieldValueClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory, this)
val continuationClassesVarsCountByType: MutableMap<IrAttributeContainer, Map<Type, Int>> = hashMapOf()
val inlineMethodGenerationLock = Any()
@@ -34,9 +34,9 @@ import java.util.concurrent.ConcurrentHashMap
*/
class MemoizedInlineClassReplacements(
private val mangleReturnTypes: Boolean,
private val irFactory: IrFactory,
private val context: JvmBackendContext
) {
irFactory: IrFactory,
context: JvmBackendContext
) : MemoizedValueClassAbstractReplacements(irFactory, context) {
private val storageManager = LockBasedStorageManager("inline-class-replacements")
private val propertyMap = ConcurrentHashMap<IrPropertySymbol, IrProperty>()
@@ -46,7 +46,7 @@ class MemoizedInlineClassReplacements(
/**
* Get a replacement for a function or a constructor.
*/
val getReplacementFunction: (IrFunction) -> IrSimpleFunction? =
override val getReplacementFunction: (IrFunction) -> IrSimpleFunction? =
storageManager.createMemoizedFunctionWithNullableValues {
when {
// Don't mangle anonymous or synthetic functions, except for generated SAM wrapper methods
@@ -0,0 +1,302 @@
/*
* 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.backend.jvm
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyTypeParameters
import org.jetbrains.kotlin.backend.jvm.ir.*
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.declarations.buildProperty
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.types.isInt
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.concurrent.ConcurrentHashMap
/**
* Keeps track of replacement functions and multi-field value class box/unbox functions.
*/
class MemoizedMultiFieldValueClassReplacements(
private val mangleReturnTypes: Boolean, // todo always false
irFactory: IrFactory,
context: JvmBackendContext
) : MemoizedValueClassAbstractReplacements(irFactory, context) { // There is only sample logic yet
private val storageManager = LockBasedStorageManager("multi-field-value-class-replacements")
private val propertyMap = ConcurrentHashMap<IrPropertySymbol, IrProperty>()
val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
internal val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
/**
* Get a replacement for a function or a constructor.
*/
override val getReplacementFunction: (IrFunction) -> IrSimpleFunction? = storageManager.createMemoizedFunctionWithNullableValues {
when {
// Don't mangle anonymous or synthetic functions, except for generated SAM wrapper methods
(it.isLocal && it is IrSimpleFunction && it.overriddenSymbols.isEmpty()) ||
(it.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR && it.visibility == DescriptorVisibilities.LOCAL) ||
it.isStaticInlineClassReplacement ||
it.origin.isSynthetic && it.origin != IrDeclarationOrigin.SYNTHETIC_GENERATED_SAM_IMPLEMENTATION ->
null
it.isMultiFieldValueClassFieldGetter ->
if (it.hasMangledReturnType)
createMethodReplacement(it)
else
null
// Mangle all functions in the body of an inline class
it.parent.safeAs<IrClass>()?.isMultiFieldValueClass == true ->
when {
it.isRemoveAtSpecialBuiltinStub() ->
null
it.isInlineClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod() ||
it.origin == IrDeclarationOrigin.IR_BUILTINS_STUB ->
createMethodReplacement(it)
else ->
createStaticReplacement(it)
}
// Otherwise, mangle functions with mangled parameters, ignoring constructors
it is IrSimpleFunction && !it.isFromJava() && (it.hasMangledParameters || mangleReturnTypes && it.hasMangledReturnType) ->
createMethodReplacement(it)
else ->
null
}
}
private fun IrFunction.isRemoveAtSpecialBuiltinStub() =
origin == IrDeclarationOrigin.IR_BUILTINS_STUB &&
name.asString() == "remove" &&
valueParameters.size == 1 &&
valueParameters[0].type.isInt()
private fun IrFunction.isInlineClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod(): Boolean {
if (this !is IrSimpleFunction) return false
if (!this.isFakeOverride) return false
val parentClass = parentClassOrNull ?: return false
if (!parentClass.isMultiFieldValueClass) return false
val overridden = resolveFakeOverride() ?: return false
if (!overridden.parentAsClass.isJvmInterface) return false
if (overridden.modality == Modality.ABSTRACT) return false
// We have a non-abstract interface member.
// It is a JVM default interface method if one of the following conditions are true:
// - it is a Java method,
// - it is a Kotlin function compiled to JVM default interface method.
return overridden.isFromJava() || overridden.isCompiledToJvmDefault(context.state.jvmDefaultMode)
}
// /**
// * Get the box function for an inline class. Concretely, this is a synthetic
// * static function named "box-impl" which takes an unboxed value and returns
// * a boxed value.
// */
// val getBoxFunction: (IrClass) -> IrSimpleFunction =
// storageManager.createMemoizedFunction { irClass ->
// require(irClass.isSingleFieldValueClass)
// irFactory.buildFun {
// name = Name.identifier(KotlinTypeMapper.BOX_JVM_METHOD_NAME)
// origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER
// returnType = irClass.defaultType
// }.apply {
// parent = irClass
// copyTypeParametersFrom(irClass)
// addValueParameter {
// name = InlineClassDescriptorResolver.BOXING_VALUE_PARAMETER_NAME
// type = irClass.inlineClassRepresentation!!.underlyingType
// }
// }
// }
//
// /**
// * Get the unbox function for an inline class. Concretely, this is a synthetic
// * member function named "unbox-impl" which returns an unboxed result.
// */
// val getUnboxFunction: (IrClass) -> IrSimpleFunction =
// storageManager.createMemoizedFunction { irClass ->
// require(irClass.isSingleFieldValueClass)
// irFactory.buildFun {
// name = Name.identifier(KotlinTypeMapper.UNBOX_JVM_METHOD_NAME)
// origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER
// returnType = irClass.inlineClassRepresentation!!.underlyingType
// }.apply {
// parent = irClass
// createDispatchReceiverParameter()
// }
// }
//
// private val specializedEqualsCache = storageManager.createCacheWithNotNullValues<IrClass, IrSimpleFunction>()
// fun getSpecializedEqualsMethod(irClass: IrClass, irBuiltIns: IrBuiltIns): IrSimpleFunction {
// require(irClass.isSingleFieldValueClass)
// return specializedEqualsCache.computeIfAbsent(irClass) {
// irFactory.buildFun {
// name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME
// // TODO: Revisit this once we allow user defined equals methods in inline classes.
// origin = JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD
// returnType = irBuiltIns.booleanType
// }.apply {
// parent = irClass
// // We ignore type arguments here, since there is no good way to go from type arguments to types in the IR anyway.
// val typeArgument =
// IrSimpleTypeImpl(null, irClass.symbol, false, List(irClass.typeParameters.size) { IrStarProjectionImpl }, listOf())
// addValueParameter {
// name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_FIRST_PARAMETER_NAME
// type = typeArgument
// }
// addValueParameter {
// name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_SECOND_PARAMETER_NAME
// type = typeArgument
// }
// }
// }
// }
private fun createMethodReplacement(function: IrFunction): IrSimpleFunction =
buildReplacement(function, function.origin) {
originalFunctionForMethodReplacement[this] = function
dispatchReceiverParameter = function.dispatchReceiverParameter?.copyTo(this, index = -1)
extensionReceiverParameter = function.extensionReceiverParameter?.copyTo(
// The function's name will be mangled, so preserve the old receiver name.
this, index = -1, name = Name.identifier(function.extensionReceiverName(context.state))
)
contextReceiverParametersCount = function.contextReceiverParametersCount
valueParameters = function.valueParameters.mapIndexed { index, parameter ->
parameter.copyTo(this, index = index, defaultValue = null).also {
// Assuming that constructors and non-override functions are always replaced with the unboxed
// equivalent, deep-copying the value here is unnecessary. See `JvmInlineClassLowering`.
it.defaultValue = parameter.defaultValue?.patchDeclarationParents(this)
}
}
}
private fun createStaticReplacement(function: IrFunction): IrSimpleFunction =
buildReplacement(function, JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT, noFakeOverride = true) {
originalFunctionForStaticReplacement[this] = function
val newValueParameters = mutableListOf<IrValueParameter>()
if (function.dispatchReceiverParameter != null) {
// FAKE_OVERRIDEs have broken dispatch receivers
newValueParameters += function.parentAsClass.thisReceiver!!.copyTo(
this, index = newValueParameters.size, name = Name.identifier("arg${newValueParameters.size}"),
type = function.parentAsClass.defaultType, origin = IrDeclarationOrigin.MOVED_DISPATCH_RECEIVER
)
}
if (function.contextReceiverParametersCount != 0) {
function.valueParameters.take(function.contextReceiverParametersCount).forEachIndexed { i, contextReceiver ->
newValueParameters += contextReceiver.copyTo(
this, index = newValueParameters.size, name = Name.identifier("contextReceiver$i"),
origin = IrDeclarationOrigin.MOVED_CONTEXT_RECEIVER
)
}
}
function.extensionReceiverParameter?.let {
newValueParameters += it.copyTo(
this, index = newValueParameters.size, name = Name.identifier(function.extensionReceiverName(context.state)),
origin = IrDeclarationOrigin.MOVED_EXTENSION_RECEIVER
)
}
for (parameter in function.valueParameters.drop(function.contextReceiverParametersCount)) {
newValueParameters += parameter.copyTo(this, index = newValueParameters.size, defaultValue = null).also {
// See comment next to a similar line above.
it.defaultValue = parameter.defaultValue?.patchDeclarationParents(this)
}
}
valueParameters = newValueParameters
}
private fun buildReplacement(
function: IrFunction,
replacementOrigin: IrDeclarationOrigin,
noFakeOverride: Boolean = false,
body: IrFunction.() -> Unit
): IrSimpleFunction {
val useOldManglingScheme = context.state.useOldManglingSchemeForFunctionsWithInlineClassesInSignatures
val replacement = buildReplacementInner(function, replacementOrigin, noFakeOverride, useOldManglingScheme, body)
// When using the new mangling scheme we might run into dependencies using the old scheme
// for which we will fall back to the old mangling scheme as well.
if (
!useOldManglingScheme &&
replacement.name.asString().contains("-") &&
function.parentClassId?.let { classFileContainsMethod(it, replacement, context) } == false
) {
return buildReplacementInner(function, replacementOrigin, noFakeOverride, true, body)
}
return replacement
}
private fun buildReplacementInner(
function: IrFunction,
replacementOrigin: IrDeclarationOrigin,
noFakeOverride: Boolean,
useOldManglingScheme: Boolean,
body: IrFunction.() -> Unit,
): IrSimpleFunction = irFactory.buildFun {
updateFrom(function)
if (function is IrConstructor) {
// The [updateFrom] call will set the modality to FINAL for constructors, while the JVM backend would use OPEN here.
modality = Modality.OPEN
}
origin = when {
function.origin == IrDeclarationOrigin.GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER ->
JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD
function is IrConstructor && function.constructedClass.isSingleFieldValueClass ->
JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_CONSTRUCTOR
else ->
replacementOrigin
}
if (noFakeOverride) {
isFakeOverride = false
}
name = InlineClassAbi.mangledNameFor(function, mangleReturnTypes, useOldManglingScheme)
returnType = function.returnType
}.apply {
parent = function.parent
annotations = function.annotations
copyTypeParameters(function.allTypeParameters)
if (function.metadata != null) {
metadata = function.metadata
function.metadata = null
}
copyAttributes(function as? IrAttributeContainer)
if (function is IrSimpleFunction) {
val propertySymbol = function.correspondingPropertySymbol
if (propertySymbol != null) {
val property = propertyMap.getOrPut(propertySymbol) {
irFactory.buildProperty {
name = propertySymbol.owner.name
updateFrom(propertySymbol.owner)
}.apply {
parent = propertySymbol.owner.parent
copyAttributes(propertySymbol.owner)
annotations = propertySymbol.owner.annotations
backingField = propertySymbol.owner.backingField
}
}
correspondingPropertySymbol = property.symbol
when (function) {
propertySymbol.owner.getter -> property.getter = this
propertySymbol.owner.setter -> property.setter = this
else -> error("Orphaned property getter/setter: ${function.render()}")
}
}
overriddenSymbols = function.overriddenSymbols.map {
getReplacementFunction(it.owner)?.symbol ?: it
}
}
body()
}
}
@@ -0,0 +1,17 @@
/*
* Copyright 2010-2022 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.backend.jvm
import org.jetbrains.kotlin.ir.declarations.IrFactory
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
abstract class MemoizedValueClassAbstractReplacements(protected val irFactory: IrFactory, protected val context: JvmBackendContext) {
/**
* Get a replacement for a function or a constructor.
*/
abstract val getReplacementFunction: (IrFunction) -> IrSimpleFunction?
}
@@ -65,6 +65,8 @@ open class IrClassImpl(
override var inlineClassRepresentation: InlineClassRepresentation<IrSimpleType>? = null
override var multiFieldValueClassRepresentation: MultiFieldValueClassRepresentation<IrSimpleType>? = null
override var metadata: MetadataSource? = null
override var attributeOwnerId: IrAttributeContainer = this
@@ -40,6 +40,8 @@ abstract class IrClass :
abstract var inlineClassRepresentation: InlineClassRepresentation<IrSimpleType>?
abstract var multiFieldValueClassRepresentation: MultiFieldValueClassRepresentation<IrSimpleType>?
abstract var sealedSubclasses: List<IrClassSymbol>
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
import org.jetbrains.kotlin.types.SimpleType
class IrLazyClass(
override val startOffset: Int,
@@ -101,9 +102,14 @@ class IrLazyClass(
}
override var inlineClassRepresentation: InlineClassRepresentation<IrSimpleType>? by lazyVar(stubGenerator.lock) {
descriptor.inlineClassRepresentation?.mapUnderlyingType {
it.toIrType() as? IrSimpleType ?: error("Inline class underlying type is not a simple type: ${render()}")
}
descriptor.inlineClassRepresentation?.mapUnderlyingType(::simplyTypeToIrOrThrow)
}
private fun simplyTypeToIrOrThrow(it: SimpleType) =
it.toIrType() as? IrSimpleType ?: error("Inline class underlying type is not a simple type: ${render()}")
override var multiFieldValueClassRepresentation: MultiFieldValueClassRepresentation<IrSimpleType>? by lazyVar(stubGenerator.lock) {
descriptor.multiFieldValueClassRepresentation?.mapUnderlyingType(::simplyTypeToIrOrThrow)
}
override var attributeOwnerId: IrAttributeContainer = this
@@ -629,6 +629,9 @@ open class IrBasedClassDescriptor(owner: IrClass) : ClassDescriptor, IrBasedDecl
override fun getInlineClassRepresentation(): InlineClassRepresentation<SimpleType>? =
owner.inlineClassRepresentation?.mapUnderlyingType { it.toIrBasedKotlinType() as SimpleType }
override fun getMultiFieldValueClassRepresentation(): MultiFieldValueClassRepresentation<SimpleType>? =
owner.multiFieldValueClassRepresentation?.mapUnderlyingType { it.toIrBasedKotlinType() as SimpleType }
override fun getOriginal() = this
override fun isExpect() = owner.isExpect
@@ -759,6 +762,8 @@ open class IrBasedEnumEntryDescriptor(owner: IrEnumEntry) : ClassDescriptor, IrB
override fun getInlineClassRepresentation(): InlineClassRepresentation<SimpleType>? = TODO("not implemented")
override fun getMultiFieldValueClassRepresentation(): MultiFieldValueClassRepresentation<SimpleType>? = TODO("not implemented")
override fun getOriginal() = this
override fun isExpect() = false
@@ -0,0 +1,21 @@
// WITH_STDLIB
// TARGET_BACKEND: JVM_IR
// WORKS_WHEN_VALUE_CLASS
// LANGUAGE: +ValueClasses
@JvmInline
value class IC(val x: UInt)
fun ic(x: IC) = x.x
@JvmInline
value class SimpleMFVC(val x: UInt, val y: IC, val z: String)
fun smfvc(ic: IC, x: SimpleMFVC, ic1: UInt) = ic(ic) + x.x + ic(x.y) + ic1
@JvmInline
value class GreaterMFVC(val x: SimpleMFVC, val y: IC, val z: SimpleMFVC)
fun gmfvc(ic: IC, x: GreaterMFVC, ic1: UInt) = smfvc(ic, x.x, 0U) + ic(x.y) + smfvc(IC(0U), x.z, ic1)
fun box() = "todo"
@@ -48207,6 +48207,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("classFlattening.kt")
public void testClassFlattening() throws Exception {
runTest("compiler/testData/codegen/box/valueClasses/classFlattening.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
}
@Test
@TestMetadata("equality.kt")
public void testEquality() throws Exception {