JVM IR: Fix inline class constructor ABI (KT-37013, KT-37015)

This commit is contained in:
Steven Schäfer
2020-06-15 18:31:21 +02:00
committed by Dmitry Petrov
parent b93c49afae
commit 846fc13519
10 changed files with 73 additions and 29 deletions
@@ -53,10 +53,15 @@ open class DefaultArgumentStubGenerator(
}
private fun lower(irFunction: IrFunction): List<IrFunction>? {
val visibility = defaultArgumentStubVisibility(irFunction)
val newIrFunction =
irFunction.generateDefaultsFunction(context, skipInlineMethods, skipExternalMethods, forceSetOverrideSymbols, visibility)
?: return null
irFunction.generateDefaultsFunction(
context,
skipInlineMethods,
skipExternalMethods,
forceSetOverrideSymbols,
defaultArgumentStubVisibility(irFunction),
useConstructorMarker(irFunction)
) ?: return null
if (newIrFunction.isFakeOverride) {
return listOf(irFunction, newIrFunction)
}
@@ -185,7 +190,8 @@ open class DefaultArgumentStubGenerator(
protected open fun IrBlockBodyBuilder.generateSuperCallHandlerCheckIfNeeded(
irFunction: IrFunction,
newIrFunction: IrFunction) {
newIrFunction: IrFunction
) {
//NO-OP Stub
}
@@ -202,6 +208,8 @@ open class DefaultArgumentStubGenerator(
protected open fun defaultArgumentStubVisibility(function: IrFunction) = Visibilities.PUBLIC
protected open fun useConstructorMarker(function: IrFunction) = function is IrConstructor
private fun log(msg: () -> String) = context.log { "DEFAULT-REPLACER: ${msg()}" }
}
@@ -310,15 +318,20 @@ open class DefaultParameterInjector(
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val declaration = expression.symbol.owner
val visibility = defaultArgumentStubVisibility(declaration)
// We *have* to find the actual function here since on the JVM, a default stub for a function implemented
// in an interface does not leave an abstract method after being moved to DefaultImpls (see InterfaceLowering).
// Calling the fake override on an implementation of that interface would then result in a call to a method
// that does not actually exist as DefaultImpls is not part of the inheritance hierarchy.
val stubFunction = declaration.findBaseFunctionWithDefaultArguments(skipInline, skipExternalMethods)
?.generateDefaultsFunction(context, skipInline, skipExternalMethods, forceSetOverrideSymbols, visibility)
?: return null
?.generateDefaultsFunction(
context,
skipInline,
skipExternalMethods,
forceSetOverrideSymbols,
defaultArgumentStubVisibility(declaration),
useConstructorMarker(declaration)
) ?: return null
log { "$declaration -> $stubFunction" }
@@ -367,6 +380,8 @@ open class DefaultParameterInjector(
protected open fun defaultArgumentStubVisibility(function: IrFunction) = Visibilities.PUBLIC
protected open fun useConstructorMarker(function: IrFunction) = function is IrConstructor
private fun log(msg: () -> String) = context.log { "DEFAULT-INJECTOR: ${msg()}" }
}
@@ -416,7 +431,8 @@ private fun IrFunction.generateDefaultsFunction(
skipInlineMethods: Boolean,
skipExternalMethods: Boolean,
forceSetOverrideSymbols: Boolean,
visibility: Visibility
visibility: Visibility,
useConstructorMarker: Boolean
): IrFunction? {
if (skipInlineMethods && isInline) return null
if (skipExternalMethods && isExternalOrInheritedFromExternal()) return null
@@ -425,7 +441,7 @@ private fun IrFunction.generateDefaultsFunction(
if (this is IrSimpleFunction) {
// If this is an override of a function with default arguments, produce a fake override of a default stub.
if (overriddenSymbols.any { it.owner.findBaseFunctionWithDefaultArguments(skipInlineMethods, skipExternalMethods) != null })
return generateDefaultsFunctionImpl(context, IrDeclarationOrigin.FAKE_OVERRIDE, visibility, isFakeOverride = true).also {
return generateDefaultsFunctionImpl(context, IrDeclarationOrigin.FAKE_OVERRIDE, visibility, true, useConstructorMarker).also {
context.mapping.defaultArgumentsDispatchFunction[this] = it
context.mapping.defaultArgumentsOriginalFunction[it] = this
@@ -436,7 +452,8 @@ private fun IrFunction.generateDefaultsFunction(
skipInlineMethods,
skipExternalMethods,
forceSetOverrideSymbols,
visibility
visibility,
useConstructorMarker
)?.symbol as IrSimpleFunctionSymbol?
}
}
@@ -454,7 +471,7 @@ private fun IrFunction.generateDefaultsFunction(
// binaries, it's way too late to fix it. Hence the workaround.
if (valueParameters.any { it.defaultValue != null }) {
return generateDefaultsFunctionImpl(
context, IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER, visibility, isFakeOverride = false
context, IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER, visibility, false, useConstructorMarker
).also {
context.mapping.defaultArgumentsDispatchFunction[this] = it
context.mapping.defaultArgumentsOriginalFunction[it] = this
@@ -467,7 +484,8 @@ private fun IrFunction.generateDefaultsFunctionImpl(
context: CommonBackendContext,
newOrigin: IrDeclarationOrigin,
newVisibility: Visibility,
isFakeOverride: Boolean
isFakeOverride: Boolean,
useConstructorMarker: Boolean
): IrFunction {
val newFunction = when (this) {
is IrConstructor ->
@@ -515,7 +533,7 @@ private fun IrFunction.generateDefaultsFunctionImpl(
for (i in 0 until (valueParameters.size + 31) / 32) {
newFunction.addValueParameter("mask$i".synthesizedString, context.irBuiltIns.intType, IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION)
}
if (this is IrConstructor) {
if (useConstructorMarker) {
val markerType = context.ir.symbols.defaultConstructorMarker.defaultType.makeNullable()
newFunction.addValueParameter("marker".synthesizedString, markerType, IrDeclarationOrigin.DEFAULT_CONSTRUCTOR_MARKER)
} else if (context.ir.shouldGenerateHandlerParameterForDefaultBodyFun()) {
@@ -31,6 +31,7 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
object SYNTHETIC_INLINE_CLASS_MEMBER : IrDeclarationOriginImpl("SYNTHETIC_INLINE_CLASS_MEMBER", isSynthetic = true)
object INLINE_CLASS_GENERATED_IMPL_METHOD : IrDeclarationOriginImpl("INLINE_CLASS_GENERATED_IMPL_METHOD")
object STATIC_INLINE_CLASS_REPLACEMENT : IrDeclarationOriginImpl("STATIC_INLINE_CLASS_REPLACEMENT")
object STATIC_INLINE_CLASS_CONSTRUCTOR : IrDeclarationOriginImpl("STATIC_INLINE_CLASS_CONSTRUCTOR")
object GENERATED_ASSERTION_ENABLED_FIELD : IrDeclarationOriginImpl("GENERATED_ASSERTION_ENABLED_FIELD", isSynthetic = true)
object GENERATED_EXTENDED_MAIN : IrDeclarationOriginImpl("GENERATED_EXTENDED_MAIN", isSynthetic = true)
object SUSPEND_IMPL_STATIC_FUNCTION : IrDeclarationOriginImpl("SUSPEND_IMPL_STATIC_FUNCTION", isSynthetic = true)
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.lower.LocalDeclarationsLowering
import org.jetbrains.kotlin.backend.common.lower.allOverridden
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.isStaticInlineClassReplacement
import org.jetbrains.kotlin.backend.jvm.lower.suspendFunctionOriginal
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.coroutines.CoroutineTransformerMethodVisitor
@@ -119,9 +120,9 @@ private fun IrFunction.isBridgeToSuspendImplMethod(): Boolean =
}
private fun IrFunction.isStaticInlineClassReplacementDelegatingCall(): Boolean =
this is IrAttributeContainer && origin != JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT &&
this is IrAttributeContainer && !isStaticInlineClassReplacement &&
parentAsClass.declarations.find { it is IrAttributeContainer && it.attributeOwnerId == attributeOwnerId && it !== this }
?.origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT
?.isStaticInlineClassReplacement == true
internal fun IrFunction.shouldContainSuspendMarkers(): Boolean = !isInvokeSuspendOfContinuation() &&
// These are tail-call bridges and do not require any bytecode modifications.
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.getJvmNameFromAnnotation
import org.jetbrains.kotlin.backend.jvm.ir.isCompiledToJvmDefault
import org.jetbrains.kotlin.backend.jvm.ir.isStaticInlineClassReplacement
import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor
import org.jetbrains.kotlin.backend.jvm.lower.suspendFunctionOriginal
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
@@ -35,7 +36,10 @@ import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.java.*
import org.jetbrains.kotlin.load.kotlin.*
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.load.kotlin.forceSingleValueParameterBoxing
import org.jetbrains.kotlin.load.kotlin.signatures
import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
@@ -314,7 +318,7 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
// remapped name or signature and forward to the actually declared method.
if (caller.origin == IrDeclarationOrigin.BRIDGE || caller.origin == IrDeclarationOrigin.BRIDGE_SPECIAL) return null
// Do not remap calls to static replacements of inline class methods, since they have completely different signatures.
if (callee.origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT) return null
if (callee.isStaticInlineClassReplacement) return null
val overriddenSpecialBuiltinFunction = callee.descriptor.original.getOverriddenBuiltinReflectingJvmDescriptor()
if (overriddenSpecialBuiltinFunction != null && !superCall) {
return mapSignatureSkipGeneric(context.referenceFunction(overriddenSpecialBuiltinFunction.original).owner)
@@ -343,3 +343,7 @@ fun IrProperty.needsAccessor(accessor: IrSimpleFunction): Boolean = when {
// We do not produce default accessors for private fields
else -> accessor.origin != IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR || !Visibilities.isPrivate(accessor.visibility)
}
val IrDeclaration.isStaticInlineClassReplacement: Boolean
get() = origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT
|| origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_CONSTRUCTOR
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.codegen.*
import org.jetbrains.kotlin.backend.jvm.ir.IrInlineReferenceLocator
import org.jetbrains.kotlin.backend.jvm.ir.defaultValue
import org.jetbrains.kotlin.backend.jvm.ir.isStaticInlineClassReplacement
import org.jetbrains.kotlin.backend.jvm.localDeclarationsPhase
import org.jetbrains.kotlin.codegen.coroutines.*
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
@@ -640,7 +641,7 @@ private fun IrFunction.suspendFunctionViewOrStub(context: JvmBackendContext): Ir
}
internal fun IrFunction.suspendFunctionOriginal(): IrFunction =
if (this is IrSimpleFunction && isSuspend && origin != JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT)
if (this is IrSimpleFunction && isSuspend && !isStaticInlineClassReplacement)
attributeOwnerId as IrFunction
else this
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.lower.irIfThen
import org.jetbrains.kotlin.backend.common.lower.irNot
import org.jetbrains.kotlin.backend.common.lower.irThrow
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.getJvmVisibilityOfDefaultArgumentStub
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
@@ -48,6 +49,9 @@ class JvmDefaultArgumentStubGenerator(override val context: JvmBackendContext) :
override fun defaultArgumentStubVisibility(function: IrFunction) = function.getJvmVisibilityOfDefaultArgumentStub()
override fun useConstructorMarker(function: IrFunction): Boolean =
function is IrConstructor || function.origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_CONSTRUCTOR
override fun IrBlockBodyBuilder.generateSuperCallHandlerCheckIfNeeded(
irFunction: IrFunction,
newIrFunction: IrFunction
@@ -7,8 +7,10 @@ package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.lower.DefaultParameterInjector
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.defaultValue
import org.jetbrains.kotlin.backend.jvm.ir.getJvmVisibilityOfDefaultArgumentStub
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.expressions.IrExpression
@@ -27,4 +29,7 @@ class JvmDefaultParameterInjector(context: JvmBackendContext) :
}
override fun defaultArgumentStubVisibility(function: IrFunction) = function.getJvmVisibilityOfDefaultArgumentStub()
override fun useConstructorMarker(function: IrFunction): Boolean =
function is IrConstructor || function.origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_CONSTRUCTOR
}
@@ -10,8 +10,10 @@ import org.jetbrains.kotlin.backend.common.ir.copyTypeParameters
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
import org.jetbrains.kotlin.backend.common.ir.createDispatchReceiverParameter
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.isStaticInlineClassReplacement
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi.mangledNameFor
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.declarations.buildFunWithDescriptorForInlining
@@ -45,7 +47,7 @@ class MemoizedInlineClassReplacements(private val mangleReturnTypes: Boolean) {
// Don't mangle anonymous or synthetic functions
it.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA ||
it.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR ||
it.origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT ||
it.isStaticInlineClassReplacement ||
it.origin.isSynthetic ||
it.isInlineClassFieldGetter -> null
@@ -181,10 +183,17 @@ class MemoizedInlineClassReplacements(private val mangleReturnTypes: Boolean) {
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 (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_INLINE_CLASS_MEMBER ->
JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD
function is IrConstructor && function.constructedClass.isInline ->
JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_CONSTRUCTOR
else ->
replacementOrigin
}
if (noFakeOverride) {
isFakeOverride = false
@@ -1,13 +1,10 @@
// !LANGUAGE: +InlineClasses
// This tests both KT-37013 and KT-37015.
inline class A(val x: Int = 0)
// 0 <init>\(\)V
// 1 private synthetic <init>\(I\)V
// 1 public final static synthetic box-impl\(I\)LA;
// JVM_TEMPLATES
// 1 public static constructor-impl\(I\)I
// 1 public static synthetic constructor-impl\$default\(IILkotlin/jvm/internal/DefaultConstructorMarker;\)I
// JVM_IR_TEMPLATES
// 1 public final static constructor-impl\(I\)I
// 1 public static synthetic constructor-impl\$default\(IILjava/lang/Object;\)I