JVM IR: Fix corresponding properties in inline classes

This commit is contained in:
Steven Schäfer
2020-04-20 17:28:35 +02:00
committed by Alexander Udalov
parent c41e71b1c5
commit 497df0922a
9 changed files with 146 additions and 48 deletions
@@ -12958,6 +12958,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/inlineClasses/passInlineClassWithSpreadOperatorToVarargs.kt");
}
@TestMetadata("propertyLoweringOrder.kt")
public void testPropertyLoweringOrder() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt");
}
@TestMetadata("referToPropertyInCompanionObjectOfInlineClass.kt")
public void testReferToPropertyInCompanionObjectOfInlineClass() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/referToPropertyInCompanionObjectOfInlineClass.kt");
@@ -9,20 +9,26 @@ 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.allOverridden
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper
import org.jetbrains.kotlin.backend.jvm.ir.eraseTypeParameters
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.*
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.inlineClassFieldName
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
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.IrCallImpl
@@ -37,6 +43,8 @@ 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.DescriptorUtils
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -65,9 +73,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
}
}
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid()
}
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid()
override fun visitClassNew(declaration: IrClass): IrStatement {
// The arguments to the primary constructor are in scope in the initializers of IrFields.
@@ -117,17 +123,6 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
}
}
private fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction) {
source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run {
irExprBody(irCall(target).apply {
passTypeArgumentsFrom(source)
for ((parameter, newParameter) in source.explicitParameters.zip(target.explicitParameters)) {
putArgument(newParameter, irGet(parameter))
}
})
}
}
private fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
replacement.valueParameters.forEach { it.transformChildrenVoid() }
replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement)
@@ -137,15 +132,26 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
if (function.overriddenSymbols.isEmpty() || replacement.dispatchReceiverParameter != null)
return listOf(replacement)
// If the original function has value parameters which need mangling we still need to replace
// it with a mangled version.
val bridgeFunction = if (!function.isFakeOverride && function.fullValueParameterList.any { it.type.requiresMangling }) {
context.inlineClassReplacements.createMethodReplacement(function)
} else {
// Update the overridden symbols to point to their inline class replacements
function.overriddenSymbols = replacement.overriddenSymbols
function
}
val bridgeFunction = createBridgeDeclaration(
function,
when {
// If the original function has value parameters which need mangling we still need to replace
// it with a mangled version.
!function.isFakeOverride && function.fullValueParameterList.any { it.type.requiresMangling } ->
replacement.name
// Since we remove the corresponding property symbol from the bridge we need to resolve getter/setter
// names at this point.
replacement.isGetter ->
Name.identifier(JvmAbi.getterName(replacement.correspondingPropertySymbol!!.owner.name.asString()))
replacement.isSetter ->
Name.identifier(JvmAbi.setterName(replacement.correspondingPropertySymbol!!.owner.name.asString()))
else ->
function.name
}
)
// Update the overridden symbols to point to their inline class replacements
bridgeFunction.overriddenSymbols = replacement.overriddenSymbols
// Replace the function body with a wrapper
if (!bridgeFunction.isFakeOverride || !bridgeFunction.parentAsClass.isInline) {
@@ -158,6 +164,35 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
return listOf(replacement, bridgeFunction)
}
// We may need to add a bridge method for inline class methods with static replacements. Ideally, we'd do this in BridgeLowering,
// but unfortunately this is a special case in the old backend. The bridge method is not marked as such and does not follow the normal
// visibility rules for bridge methods.
private fun createBridgeDeclaration(source: IrSimpleFunction, mangledName: Name) =
buildFun {
updateFrom(source)
name = mangledName
returnType = source.returnType
}.apply {
copyParameterDeclarationsFrom(source)
annotations += source.annotations
parent = source.parent
// We need to ensure that this bridge has the same attribute owner as its static inline class replacement, since this
// is used in [CoroutineCodegen.isStaticInlineClassReplacementDelegatingCall] to identify the bridge and avoid generating
// a continuation class.
copyAttributes(source)
}
private fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction) {
source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run {
irExprBody(irCall(target).apply {
passTypeArgumentsFrom(source)
for ((parameter, newParameter) in source.explicitParameters.zip(target.explicitParameters)) {
putArgument(newParameter, irGet(parameter))
}
})
}
}
// 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> {
@@ -15,10 +15,12 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.declarations.buildFunWithDescriptorForInlining
import org.jetbrains.kotlin.ir.builders.declarations.buildProperty
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionBase
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
import org.jetbrains.kotlin.ir.util.*
@@ -32,6 +34,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
*/
class MemoizedInlineClassReplacements {
private val storageManager = LockBasedStorageManager("inline-class-replacements")
private val propertyMap = mutableMapOf<IrPropertySymbol, IrProperty>()
/**
* Get a replacement for a function or a constructor.
@@ -124,7 +127,7 @@ class MemoizedInlineClassReplacements {
}
}
internal fun createMethodReplacement(function: IrFunction): IrSimpleFunction =
private fun createMethodReplacement(function: IrFunction): IrSimpleFunction =
buildReplacement(function, function.origin) {
require(function.dispatchReceiverParameter != null && function is IrSimpleFunction)
val newValueParameters = ArrayList<IrValueParameter>()
@@ -176,33 +179,49 @@ class MemoizedInlineClassReplacements {
replacementOrigin: IrDeclarationOrigin,
noFakeOverride: Boolean = false,
body: IrFunctionImpl.() -> Unit
) =
buildFunWithDescriptorForInlining(function.descriptor) {
updateFrom(function)
origin = if (function.origin == IrDeclarationOrigin.GENERATED_INLINE_CLASS_MEMBER) {
JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD
} else {
replacementOrigin
}
if (noFakeOverride) {
isFakeOverride = false
}
name = mangledNameFor(function)
returnType = function.returnType
}.apply {
parent = function.parent
annotations += function.annotations
copyTypeParameters(function.allTypeParameters)
metadata = function.metadata
function.safeAs<IrFunctionBase<*>>()?.metadata = null
) = buildFunWithDescriptorForInlining(function.descriptor) {
updateFrom(function)
origin = if (function.origin == IrDeclarationOrigin.GENERATED_INLINE_CLASS_MEMBER) {
JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD
} else {
replacementOrigin
}
if (noFakeOverride) {
isFakeOverride = false
}
name = mangledNameFor(function)
returnType = function.returnType
}.apply {
parent = function.parent
annotations += function.annotations
copyTypeParameters(function.allTypeParameters)
metadata = function.metadata
function.safeAs<IrFunctionBase<*>>()?.metadata = null
if (function is IrSimpleFunction) {
correspondingPropertySymbol = function.correspondingPropertySymbol
overriddenSymbols = function.overriddenSymbols.map {
getReplacementFunction(it.owner)?.symbol ?: it
if (function is IrSimpleFunction) {
val propertySymbol = function.correspondingPropertySymbol
if (propertySymbol != null) {
val property = propertyMap.getOrPut(propertySymbol) {
buildProperty(propertySymbol.descriptor) {
name = propertySymbol.owner.name
updateFrom(propertySymbol.owner)
}.apply {
parent = propertySymbol.owner.parent
}
}
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()}")
}
}
body()
overriddenSymbols = function.overriddenSymbols.map {
getReplacementFunction(it.owner)?.symbol ?: it
}
}
body()
}
}
@@ -0,0 +1,14 @@
// FILE: 1.kt
inline class A(val x: String)
fun accessProperty(y: B): A {
y.a = A("OK")
return y.a
}
// FILE: 2.kt
class B(var a: A)
fun box(): String = accessProperty(B(A("Fail"))).x
@@ -14173,6 +14173,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/inlineClasses/passInlineClassWithSpreadOperatorToVarargs.kt");
}
@TestMetadata("propertyLoweringOrder.kt")
public void testPropertyLoweringOrder() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt");
}
@TestMetadata("referToPropertyInCompanionObjectOfInlineClass.kt")
public void testReferToPropertyInCompanionObjectOfInlineClass() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/referToPropertyInCompanionObjectOfInlineClass.kt");
@@ -14178,6 +14178,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/inlineClasses/passInlineClassWithSpreadOperatorToVarargs.kt");
}
@TestMetadata("propertyLoweringOrder.kt")
public void testPropertyLoweringOrder() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt");
}
@TestMetadata("referToPropertyInCompanionObjectOfInlineClass.kt")
public void testReferToPropertyInCompanionObjectOfInlineClass() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/referToPropertyInCompanionObjectOfInlineClass.kt");
@@ -12958,6 +12958,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/inlineClasses/passInlineClassWithSpreadOperatorToVarargs.kt");
}
@TestMetadata("propertyLoweringOrder.kt")
public void testPropertyLoweringOrder() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt");
}
@TestMetadata("referToPropertyInCompanionObjectOfInlineClass.kt")
public void testReferToPropertyInCompanionObjectOfInlineClass() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/referToPropertyInCompanionObjectOfInlineClass.kt");
@@ -11148,6 +11148,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/inlineClasses/passInlineClassWithSpreadOperatorToVarargs.kt");
}
@TestMetadata("propertyLoweringOrder.kt")
public void testPropertyLoweringOrder() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt");
}
@TestMetadata("referToPropertyInCompanionObjectOfInlineClass.kt")
public void testReferToPropertyInCompanionObjectOfInlineClass() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/referToPropertyInCompanionObjectOfInlineClass.kt");
@@ -11213,6 +11213,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/inlineClasses/passInlineClassWithSpreadOperatorToVarargs.kt");
}
@TestMetadata("propertyLoweringOrder.kt")
public void testPropertyLoweringOrder() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt");
}
@TestMetadata("referToPropertyInCompanionObjectOfInlineClass.kt")
public void testReferToPropertyInCompanionObjectOfInlineClass() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/referToPropertyInCompanionObjectOfInlineClass.kt");