[JVM_IR] Propagate Type Parameters to DefaultImpls

This ensures correct generation of generic signatures in the resulting
byte code, but it _is_ a work in progress: the actual type *arguments*
passed for these parameters during compilation are dummy `Any?` types.

Sites that need more work are indicated with TODO's.

- copy type parameters of interfaces to methods moved to DefaultImpls
- implement type parameter renaming scheme from JVM, with proper
  renaming and substitution.
- adjust call sites in bridges in classes->DefaultImpls
- adjust call sites in bridges from DefaultImpls->Interface
- adjust call sites in bridges from DefaultImpls->DefaultImpls
- adjust super calls ->DefaultImpls
- adjust calls in code of Interfaces->DefaultImpls
This commit is contained in:
Kristoffer Andersen
2020-01-08 18:02:45 +01:00
committed by Georgy Bronnikov
parent 3606a4104b
commit 4dd794c2d2
12 changed files with 184 additions and 36 deletions
@@ -16,6 +16,8 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.Scope
import org.jetbrains.kotlin.ir.builders.declarations.IrTypeParameterBuilder
import org.jetbrains.kotlin.ir.builders.declarations.build
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
@@ -31,12 +33,12 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeBuilder
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.buildSimpleType
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
import java.io.StringWriter
@@ -121,7 +123,7 @@ val IrClass.isFinalClass: Boolean
fun IrCall.getAnnotationClass(): IrClass {
val callable = symbol.owner
assert(callable is IrConstructor) { "Constructor call expected, got ${ir2string(this)}" }
val annotationClass = callable.parentAsClass
val annotationClass = callable.parentAsClass
assert(annotationClass.isAnnotationClass) { "Annotation class expected, got ${ir2string(annotationClass)}" }
return annotationClass
}
@@ -135,9 +137,11 @@ fun IrValueParameter.copyTo(
startOffset: Int = this.startOffset,
endOffset: Int = this.endOffset,
name: Name = this.name,
remapTypeMap: Map<IrTypeParameter, IrTypeParameter> = mapOf(),
type: IrType = this.type.remapTypeParameters(
(parent as IrTypeParametersContainer).classIfConstructor,
irFunction.classIfConstructor
(parent as IrTypeParametersContainer).classIfConstructor,
irFunction.classIfConstructor,
remapTypeMap
),
varargElementType: IrType? = this.varargElementType, // TODO: remapTypeParameters here as well
defaultValue: IrExpressionBody? = this.defaultValue,
@@ -395,7 +399,8 @@ fun Scope.createTemporaryVariableWithWrappedDescriptor(
irExpression: IrExpression,
nameHint: String? = null,
isMutable: Boolean = false,
origin: IrDeclarationOrigin = IrDeclarationOrigin.IR_TEMPORARY_VARIABLE): IrVariable {
origin: IrDeclarationOrigin = IrDeclarationOrigin.IR_TEMPORARY_VARIABLE
): IrVariable {
val descriptor = WrappedVariableDescriptor()
return createTemporaryVariableWithGivenDescriptor(
@@ -440,7 +445,7 @@ fun IrClass.simpleFunctions() = declarations.flatMap {
}
fun IrClass.createParameterDeclarations() {
assert (thisReceiver == null)
assert(thisReceiver == null)
thisReceiver = WrappedReceiverParameterDescriptor().let {
IrValueParameterImpl(
@@ -480,8 +485,9 @@ fun IrFunction.createDispatchReceiverParameter(origin: IrDeclarationOrigin? = nu
val IrFunction.allParameters: List<IrValueParameter>
get() = if (this is IrConstructor) {
listOf(this.constructedClass.thisReceiver
?: error(this.descriptor)
listOf(
this.constructedClass.thisReceiver
?: error(this.descriptor)
) + explicitParameters
} else {
explicitParameters
@@ -556,7 +562,8 @@ fun createStaticFunctionWithReceivers(
origin: IrDeclarationOrigin = oldFunction.origin,
modality: Modality = Modality.FINAL,
visibility: Visibility = oldFunction.visibility,
copyMetadata: Boolean = true
copyMetadata: Boolean = true,
typeParametersFromContext: List<IrTypeParameter> = listOf()
): IrSimpleFunction {
val descriptor = (oldFunction.descriptor as? DescriptorWithContainerSource)?.let {
WrappedFunctionDescriptorWithContainerSource(it.containerSource)
@@ -580,7 +587,19 @@ fun createStaticFunctionWithReceivers(
descriptor.bind(this)
parent = irParent
copyTypeParametersFrom(oldFunction)
val newTypeParametersFromContext = copyAndRenameConflictingTypeParametersFrom(
typeParametersFromContext,
oldFunction.typeParameters
)
val newTypeParametersFromFunction = copyTypeParametersFrom(oldFunction)
val typeParameterMap =
(typeParametersFromContext + oldFunction.typeParameters)
.zip(newTypeParametersFromContext + newTypeParametersFromFunction).toMap()
fun remap(type: IrType): IrType =
type.remapTypeParameters(oldFunction, this, typeParameterMap)
typeParameters.forEach { it.superTypes.replaceAll { remap(it) } }
annotations.addAll(oldFunction.annotations)
@@ -589,22 +608,74 @@ fun createStaticFunctionWithReceivers(
this,
name = Name.identifier("this"),
index = offset++,
type = dispatchReceiverType!!,
type = remap(dispatchReceiverType!!),
origin = IrDeclarationOrigin.MOVED_DISPATCH_RECEIVER
)
val extensionReceiver = oldFunction.extensionReceiverParameter?.copyTo(
this,
name = Name.identifier("receiver"),
index = offset++,
origin = IrDeclarationOrigin.MOVED_EXTENSION_RECEIVER
origin = IrDeclarationOrigin.MOVED_EXTENSION_RECEIVER,
remapTypeMap = typeParameterMap
)
valueParameters.addAll(listOfNotNull(dispatchReceiver, extensionReceiver) +
oldFunction.valueParameters.map { it.copyTo(this, index = it.index + offset) }
oldFunction.valueParameters.map {
it.copyTo(
this,
index = it.index + offset,
remapTypeMap = typeParameterMap
)
}
)
if (copyMetadata) metadata = oldFunction.metadata
}
}
/**
* Appends the parameters in [contextParameters] to the type parameters of
* [this] function, renaming those that may clash with a provided collection of
* [existingParameters] (e.g. type parameters of the function itself, when
* creating DefaultImpls).
*
* @returns List of newly created, possibly renamed, copies of type parameters
* in order of the corresponding parameters in [context].
*/
private fun IrSimpleFunction.copyAndRenameConflictingTypeParametersFrom(
contextParameters: List<IrTypeParameter>,
existingParameters: Collection<IrTypeParameter>
): List<IrTypeParameter> {
val newParameters = mutableListOf<IrTypeParameter>()
val existingNames =
(contextParameters.map { it.name.asString() } + existingParameters.map { it.name.asString() }).toMutableSet()
contextParameters.forEach { contextType ->
val newName = if (existingParameters.any { it.name.asString() == contextType.name.asString() }) {
val newNamePrefix = contextType.name.asString() + "_I"
val newName = newNamePrefix + generateSequence(1) { x -> x + 1 }.first { n ->
(newNamePrefix + n) !in existingNames
}
existingNames.add(newName)
newName
} else {
contextType.name.asString()
}
val newTypeParameter = IrTypeParameterBuilder().run {
updateFrom(contextType)
name = Name.identifier(newName)
build()
}.also {
it.parent = this
}
typeParameters.add(newTypeParameter)
newParameters.add(newTypeParameter)
}
return newParameters
}
val IrSymbol.isSuspend: Boolean
get() = this is IrSimpleFunctionSymbol && owner.isSuspend
@@ -210,11 +210,14 @@ class JvmDeclarationFactory(
},
// Old backend doesn't generate ACC_FINAL on DefaultImpls methods.
modality = Modality.OPEN,
// Interface functions are always public, with one exception: clone in Cloneable, which is protected. However, Cloneable
// has no DefaultImpls, so this merely replicates the incorrect behavior of the old backend. We should rather not generate
// a bridge to clone when interface inherits from Cloneable at all. Below, we force everything, including those bridges,
// to be public so that we won't try to generate synthetic accessor for them.
visibility = Visibilities.PUBLIC
visibility = Visibilities.PUBLIC,
typeParametersFromContext = parent.typeParameters
).also { it.copyAttributes(interfaceFun) }
}
}
@@ -209,3 +209,51 @@ fun IrBody.replaceThisByStaticReference(
return super.visitGetValue(expression)
}
}, null)
// TODO: Interface Parameters
//
// The call sites using this function share that they are calling an
// interface method that has been moved to a DefaultImpls class. In that
// process, the type parameters of the interface are introduced as the first
// parameters to the method. When rewriting calls to point to the new method,
// the instantiation `S,T` of the interface type `I<S,T>` for the _calling_
// class `C` gives the proper instantiation fo arguments.
//
// We essentially want to answer the type query:
//
// C <: I<?S,?T>
//
// And put that instantiation as the first type parameters to the call, filling
// in whatever type arguments are provided at call the call site for the rest.
// The front-end type checking guarantees this is well-formed.
//
// For now, we put `Any?`.
fun createPlaceholderAnyNType(irBuiltIns: IrBuiltIns): IrType =
irBuiltIns.anyNType
fun createDelegatingCallWithPlaceholderTypeArguments(existingCall: IrCall, redirectTarget: IrFunction, irBuiltIns: IrBuiltIns): IrCall =
IrCallImpl(
existingCall.startOffset,
existingCall.endOffset,
existingCall.type,
redirectTarget.symbol,
typeArgumentsCount = redirectTarget.typeParameters.size,
valueArgumentsCount = redirectTarget.valueParameters.size,
origin = existingCall.origin
).apply {
copyValueArgumentsFrom(
existingCall,
existingCall.symbol.owner,
this.symbol.owner,
receiversAsArguments = true,
argumentsAsReceivers = false
)
var offset = 0
existingCall.symbol.owner.parentAsClass.typeParameters.forEach { _ ->
putTypeArgument(offset++, createPlaceholderAnyNType(irBuiltIns))
}
for (i in 0 until (existingCall.typeArgumentsCount)) {
putTypeArgument(i + offset, existingCall.getTypeArgument(i))
}
}
@@ -14,6 +14,8 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
import org.jetbrains.kotlin.backend.jvm.ir.createDelegatingCallWithPlaceholderTypeArguments
import org.jetbrains.kotlin.backend.jvm.ir.createPlaceholderAnyNType
import org.jetbrains.kotlin.backend.jvm.ir.hasJvmDefault
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
@@ -49,11 +51,11 @@ private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendCo
element.acceptChildrenVoid(this)
}
override fun lower(declaration: IrClass) {
if (!declaration.isJvmInterface)
generateInterfaceMethods(declaration)
override fun lower(irClass: IrClass) {
if (!irClass.isJvmInterface)
generateInterfaceMethods(irClass)
super.visitClass(declaration)
super.visitClass(irClass)
}
private fun generateInterfaceMethods(irClass: IrClass) {
@@ -87,8 +89,12 @@ private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendCo
irFunction.body = irBlockBody {
+irReturn(
irCall(defaultImplFun.symbol, irFunction.returnType).apply {
interfaceImplementation.parentAsClass.typeParameters.forEachIndexed { index, _ ->
putTypeArgument(index, createPlaceholderAnyNType(context.irBuiltIns))
}
passTypeArgumentsFrom(irFunction, offset = interfaceImplementation.parentAsClass.typeParameters.size)
var offset = 0
passTypeArgumentsFrom(irFunction)
irFunction.dispatchReceiverParameter?.let { putValueArgument(offset++, irGet(it)) }
irFunction.extensionReceiverParameter?.let { putValueArgument(offset++, irGet(it)) }
irFunction.valueParameters.mapIndexed { i, parameter -> putValueArgument(i + offset, irGet(parameter)) }
@@ -118,12 +124,14 @@ private class InterfaceSuperCallsLowering(val context: JvmBackendContext) : IrEl
return super.visitCall(expression)
}
// TODO: This is too eagerly resolving the fake override statically. It
// should cf the old backend call precisely <supertype>.foo, not
// resolve foo to its implementation.
val superCallee = (expression.symbol.owner as IrSimpleFunction).resolveFakeOverride()!!
if (superCallee.isDefinitelyNotDefaultImplsMethod() || superCallee.hasJvmDefault()) return super.visitCall(expression)
val redirectTarget = context.declarationFactory.getDefaultImplsFunction(superCallee)
val newCall = irCall(expression, redirectTarget, receiversAsArguments = true)
val newCall = createDelegatingCallWithPlaceholderTypeArguments(expression, redirectTarget, context.irBuiltIns)
return super.visitCall(newCall)
}
}
@@ -157,7 +165,7 @@ private class InterfaceDefaultCallsLowering(val context: JvmBackendContext) : Ir
// gets redirected to call itself.
if (redirectTarget == currentFunction?.irElement) return super.visitCall(expression)
val newCall = irCall(expression, redirectTarget, receiversAsArguments = true)
val newCall = createDelegatingCallWithPlaceholderTypeArguments(expression, redirectTarget, context.irBuiltIns)
return super.visitCall(newCall)
}
@@ -8,10 +8,11 @@ package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.ir.isMethodOfAny
import org.jetbrains.kotlin.backend.common.ir.moveBodyTo
import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
import org.jetbrains.kotlin.backend.jvm.ir.createDelegatingCallWithPlaceholderTypeArguments
import org.jetbrains.kotlin.backend.jvm.ir.createPlaceholderAnyNType
import org.jetbrains.kotlin.backend.jvm.ir.hasJvmDefault
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
@@ -24,6 +25,7 @@ import org.jetbrains.kotlin.ir.expressions.IrReturn
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrLocalDelegatedPropertySymbol
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
@@ -187,11 +189,15 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
newFunction.parentAsClass.declarations.add(newFunction)
}
// Bridge from static to static method - simply fill the arguments to the parameters.
// Bridge from static to static method - simply fill the function arguments to the parameters.
// By nature of the generation of both source and target of bridge, they line up.
private fun IrFunction.bridgeToStatic(callTarget: IrFunction) {
body = IrExpressionBodyImpl(IrCallImpl(startOffset, endOffset, returnType, callTarget.symbol).also { call ->
call.passTypeArgumentsFrom(this)
callTarget.typeParameters.forEachIndexed { i, _ ->
call.putTypeArgument(i, createPlaceholderAnyNType(context.irBuiltIns))
}
valueParameters.forEachIndexed { i, it ->
call.putValueArgument(i, IrGetValueImpl(startOffset, endOffset, it.symbol))
}
@@ -209,7 +215,9 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
callTarget.symbol,
superQualifierSymbol = callTarget.parentAsClass.symbol
).also { call ->
call.passTypeArgumentsFrom(this)
this.typeParameters.drop(callTarget.parentAsClass.typeParameters.size).forEachIndexed { i, typeParameter ->
call.putTypeArgument(i, typeParameter.defaultType)
}
var offset = 0
callTarget.dispatchReceiverParameter?.let {
@@ -243,7 +251,7 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
val newFunction = removedFunctions[expression.symbol]?.owner
return super.visitCall(
if (newFunction != null) {
irCall(expression, newFunction, receiversAsArguments = true)
createDelegatingCallWithPlaceholderTypeArguments(expression, newFunction, context.irBuiltIns)
} else {
expression
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.ir.builders.declarations
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.SmartList
@@ -14,4 +15,12 @@ class IrTypeParameterBuilder : IrDeclarationBuilder() {
var variance: Variance = Variance.INVARIANT
var isReified: Boolean = false
val superTypes: MutableList<IrType> = SmartList()
fun updateFrom(from: IrTypeParameter) {
super.updateFrom(from)
index = from.index
variance = from.variance
isReified = from.isReified
superTypes.addAll(from.superTypes)
}
}
@@ -583,7 +583,16 @@ private fun IrMemberAccessExpression.copyTypeAndValueArgumentsFrom(
argumentsAsReceivers: Boolean = false
) {
copyTypeArgumentsFrom(src)
copyValueArgumentsFrom(src, srcFunction, destFunction, receiversAsArguments, argumentsAsReceivers)
}
fun IrMemberAccessExpression.copyValueArgumentsFrom(
src: IrMemberAccessExpression,
srcFunction: IrFunction,
destFunction: IrFunction,
receiversAsArguments: Boolean = false,
argumentsAsReceivers: Boolean = false
) {
var destValueArgumentIndex = 0
var srcValueArgumentIndex = 0
@@ -1,5 +1,4 @@
// IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FILE: J.java
@@ -1,5 +1,4 @@
// IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
class B<M>
interface A<T, Y : B<T>> {
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
class B<M>
interface A<T, Y : B<T>, T_I1: T> {
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
class B<M>
interface A<T, Y : B<T>> {