Support init blocks in inline classes

#KT-43198 Fixed.
This commit is contained in:
SvyatoslavScherbina
2020-11-18 14:57:36 +03:00
committed by Stanislav Erokhin
parent 8c8974e1fd
commit bbbdf14ff0
@@ -23,9 +23,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrPropertyImpl
import org.jetbrains.kotlin.ir.descriptors.WrappedPropertyDescriptor import org.jetbrains.kotlin.ir.descriptors.WrappedPropertyDescriptor
import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor
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.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrPropertySymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrPropertySymbolImpl
@@ -35,7 +33,6 @@ import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addIfNotNull
/** /**
* Boxes and unboxes values of value types when necessary. * Boxes and unboxes values of value types when necessary.
@@ -237,8 +234,13 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
buildUnboxFunction(declaration, context.getUnboxFunction(declaration)) buildUnboxFunction(declaration, context.getUnboxFunction(declaration))
} }
declaration.constructors.filter { !it.isPrimary }.toList().mapTo(declaration.declarations) { if (declaration.isNativePrimitiveType()) {
context.getLoweredInlineClassConstructor(it) // Constructors for these types aren't used and actually are malformed (e.g. lack the parameter).
// Skipping here for simplicity.
} else {
declaration.constructors.toList().mapTo(declaration.declarations) {
context.getLoweredInlineClassConstructor(it)
}
} }
} }
@@ -266,21 +268,14 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
super.visitSetField(expression) super.visitSetField(expression)
return if (expression.symbol.owner.parentClassOrNull?.isInlined() == true) { return if (expression.symbol.owner.parentClassOrNull?.isInlined() == true) {
// TODO: it is better to get rid of functions setting such fields. // Happens in one of the cases:
// Here we're trying to maintain all IR nodes as is, albeit the transformed IR isn't equivalent to the original. // 1. In primary constructor of the inlined class. Makes no sense, "has no effect", can be removed.
// By far SET_FIELD can only be in the constructor which won't be codegened. // The constructor will be lowered and used.
// Box functions use createUninitializedInstance instead of constructor calls // 2. In setter of NativePointed.rawPtr. It is generally a hack and isn't actually used.
// and are placed separately so they won't be processed here. // TODO: it is better to get rid of it.
val startOffset = expression.startOffset //
val endOffset = expression.endOffset // So drop the entire IrSetField:
IrBlockImpl(startOffset, endOffset, irBuiltIns.unitType).apply { return builder.irComposite(expression) {}
statements.addIfNotNull(expression.receiver)
statements += expression.value
statements += IrCallImpl(startOffset, endOffset, irBuiltIns.nothingType, symbols.throwNullPointerException,
symbols.throwNullPointerException.owner.typeParameters.size,
symbols.throwNullPointerException.owner.valueParameters.size)
statements += IrGetObjectValueImpl(startOffset, endOffset, irBuiltIns.unitType, irBuiltIns.unitClass)
}
} else { } else {
expression expression
} }
@@ -301,8 +296,11 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
super.visitConstructor(declaration) super.visitConstructor(declaration)
if (declaration.constructedClass.isInlined()) { if (declaration.constructedClass.isInlined()) {
if (!declaration.isPrimary) { if (declaration.constructedClass.isNativePrimitiveType()) {
buildLoweredSecondaryConstructor(declaration) // Constructors for these types aren't used and actually are malformed (e.g. lack the parameter).
// Skipping here for simplicity.
} else {
buildLoweredConstructor(declaration)
} }
// TODO: fix DFG building and nullify the body instead. // TODO: fix DFG building and nullify the body instead.
(declaration.body as IrBlockBody).statements.clear() (declaration.body as IrBlockBody).statements.clear()
@@ -431,22 +429,35 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
private fun IrBuilderWithScope.lowerConstructorCallToValue( private fun IrBuilderWithScope.lowerConstructorCallToValue(
expression: IrMemberAccessExpression<*>, expression: IrMemberAccessExpression<*>,
callee: IrConstructor callee: IrConstructor
): IrExpression = if (callee.isPrimary) { ): IrExpression {
expression.getValueArgument(0)!! this.at(expression)
} else { val loweredConstructor = this@InlineClassTransformer.context.getLoweredInlineClassConstructor(callee)
this.at(expression).irCall(this@InlineClassTransformer.context.getLoweredInlineClassConstructor(callee)).apply { return if (callee.isPrimary) this.irBlock {
val argument = irTemporary(expression.getValueArgument(0)!!)
+irCall(loweredConstructor).apply {
putValueArgument(0, irGet(argument))
}
+irGet(argument)
} else this.irCall(loweredConstructor).apply {
(0 until expression.valueArgumentsCount).forEach { (0 until expression.valueArgumentsCount).forEach {
putValueArgument(it, expression.getValueArgument(it)!!) putValueArgument(it, expression.getValueArgument(it)!!)
} }
} }
} }
private fun buildLoweredSecondaryConstructor(irConstructor: IrConstructor) { private fun buildLoweredConstructor(irConstructor: IrConstructor) {
val result = context.getLoweredInlineClassConstructor(irConstructor) val result = context.getLoweredInlineClassConstructor(irConstructor)
val irClass = irConstructor.parentAsClass val irClass = irConstructor.parentAsClass
result.body = context.createIrBuilder(result.symbol).irBlockBody(result) { result.body = context.createIrBuilder(result.symbol).irBlockBody(result) {
lateinit var thisVar: IrVariable lateinit var thisVar: IrValueDeclaration
fun IrBuilderWithScope.genReturnValue(): IrExpression = if (irConstructor.isPrimary) {
irGetObject(irBuiltIns.unitClass)
} else {
irGet(thisVar)
}
val parameterMapping = result.valueParameters.associateBy { val parameterMapping = result.valueParameters.associateBy {
irConstructor.valueParameters[it.index].symbol irConstructor.valueParameters[it.index].symbol
} }
@@ -457,9 +468,14 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression { override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
expression.transformChildrenVoid() expression.transformChildrenVoid()
val value = lowerConstructorCallToValue(expression, expression.symbol.owner)
return irBlock(expression) { return irBlock(expression) {
thisVar = irTemporary(value) thisVar = if (irConstructor.isPrimary) {
// Note: block is empty in this case.
result.valueParameters.single()
} else {
val value = lowerConstructorCallToValue(expression, expression.symbol.owner)
irTemporary(value)
}
} }
} }
@@ -484,7 +500,7 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
if (expression.returnTargetSymbol == irConstructor.symbol) { if (expression.returnTargetSymbol == irConstructor.symbol) {
return irReturn(irBlock(expression.startOffset, expression.endOffset) { return irReturn(irBlock(expression.startOffset, expression.endOffset) {
+expression.value +expression.value
+irGet(thisVar) +genReturnValue()
}) })
} }
@@ -492,7 +508,7 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
} }
}) })
} }
+irReturn(irGet(thisVar)) +irReturn(genReturnValue())
} }
} }
@@ -502,7 +518,16 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
private val Context.getLoweredInlineClassConstructor: (IrConstructor) -> IrSimpleFunction by Context.lazyMapMember { irConstructor -> private val Context.getLoweredInlineClassConstructor: (IrConstructor) -> IrSimpleFunction by Context.lazyMapMember { irConstructor ->
require(irConstructor.constructedClass.isInlined()) require(irConstructor.constructedClass.isInlined())
require(!irConstructor.isPrimary)
val returnType = if (irConstructor.isPrimary) {
// Optimization. When constructor is primary, the return value will be the same as the argument.
// So we can just use the argument on the call site.
// This might be especially important for reference types,
// to avoid redundant suboptimal "slot" machinery messing with this code.
irBuiltIns.unitType
} else {
irConstructor.returnType
}
val descriptor = WrappedSimpleFunctionDescriptor() val descriptor = WrappedSimpleFunctionDescriptor()
IrFunctionImpl( IrFunctionImpl(
@@ -516,7 +541,7 @@ private val Context.getLoweredInlineClassConstructor: (IrConstructor) -> IrSimpl
isExternal = false, isExternal = false,
isTailrec = false, isTailrec = false,
isSuspend = false, isSuspend = false,
returnType = irConstructor.returnType, returnType = returnType,
isExpect = false, isExpect = false,
isFakeOverride = false, isFakeOverride = false,
isOperator = false, isOperator = false,