Approximate types for lambda literals before serialization

This commit is contained in:
Mikhail Zarechenskiy
2020-05-29 01:14:28 +03:00
parent 366ed7d4ca
commit 0ab9b3639b
12 changed files with 224 additions and 15 deletions
@@ -259,7 +259,9 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
Method method = v.getSerializationBindings().get(METHOD_FOR_FUNCTION, frontendFunDescriptor);
assert method != null : "No method for " + frontendFunDescriptor;
FunctionDescriptor freeLambdaDescriptor = FakeDescriptorsForReferencesKt.createFreeFakeLambdaDescriptor(frontendFunDescriptor);
FunctionDescriptor freeLambdaDescriptor = FakeDescriptorsForReferencesKt.createFreeFakeLambdaDescriptor(
frontendFunDescriptor, state.getTypeApproximator()
);
v.getSerializationBindings().put(METHOD_FOR_FUNCTION, freeLambdaDescriptor, method);
DescriptorSerializer serializer =
@@ -727,7 +727,9 @@ class CoroutineCodegenForNamedFunction private constructor(
writeKotlinMetadata(v, state, KotlinClassHeader.Kind.SYNTHETIC_CLASS, 0) { av ->
val serializer = DescriptorSerializer.createForLambda(JvmSerializerExtension(v.serializationBindings, state))
val functionProto =
serializer.functionProto(createFreeFakeLambdaDescriptor(suspendFunctionJvmView))?.build() ?: return@writeKotlinMetadata
serializer.functionProto(
createFreeFakeLambdaDescriptor(suspendFunctionJvmView, state.typeApproximator)
)?.build() ?: return@writeKotlinMetadata
AsmUtil.writeAnnotationData(av, serializer, functionProto)
}
}
@@ -17,22 +17,20 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.*
import org.jetbrains.kotlin.serialization.DescriptorSerializer
import org.jetbrains.kotlin.types.*
import java.util.*
/**
* Given a function descriptor, creates another function descriptor with type parameters copied from outer context(s).
* This is needed because once we're serializing this to a proto, there's no place to store information about external type parameters.
*/
fun createFreeFakeLambdaDescriptor(descriptor: FunctionDescriptor): FunctionDescriptor {
return createFreeDescriptor(descriptor)
fun createFreeFakeLambdaDescriptor(descriptor: FunctionDescriptor, typeApproximator: TypeApproximator?): FunctionDescriptor {
return createFreeDescriptor(descriptor, typeApproximator)
}
private fun <D : CallableMemberDescriptor> createFreeDescriptor(descriptor: D): D {
private fun <D : CallableMemberDescriptor> createFreeDescriptor(descriptor: D, typeApproximator: TypeApproximator?): D {
@Suppress("UNCHECKED_CAST")
val builder = descriptor.newCopyBuilder() as CallableMemberDescriptor.CopyBuilder<D>
@@ -49,7 +47,76 @@ private fun <D : CallableMemberDescriptor> createFreeDescriptor(descriptor: D):
container = container.containingDeclaration
}
return if (typeParameters.isEmpty()) descriptor else builder.build()!!
val approximated = typeApproximator?.approximate(descriptor, builder) ?: false
return if (typeParameters.isEmpty() && !approximated) descriptor else builder.build()!!
}
private fun TypeApproximator.approximate(descriptor: CallableMemberDescriptor, builder: CallableMemberDescriptor.CopyBuilder<*>): Boolean {
var approximated = false
val returnType = descriptor.returnType
if (returnType != null) {
// unwrap to avoid instances of DeferredType
val approximatedType = approximate(returnType.unwrap(), toSuper = true)
if (approximatedType != null) {
builder.setReturnType(approximatedType)
approximated = true
}
}
if (builder !is FunctionDescriptor.CopyBuilder<*>) return approximated
val extensionReceiverParameter = descriptor.extensionReceiverParameter
if (extensionReceiverParameter != null) {
val approximatedExtensionReceiver = approximate(extensionReceiverParameter.type.unwrap(), toSuper = false)
if (approximatedExtensionReceiver != null) {
builder.setExtensionReceiverParameter(
extensionReceiverParameter.substituteTopLevelType(approximatedExtensionReceiver)
)
approximated = true
}
}
var valueParameterApproximated = false
val newParameters = descriptor.valueParameters.map {
val approximatedType = approximate(it.type.unwrap(), toSuper = false)
if (approximatedType != null) {
valueParameterApproximated = true
// invoking constructor explicitly as substitution on value parameters is not supported
ValueParameterDescriptorImpl(
it.containingDeclaration, it.original, it.index, it.annotations,
it.name, outType = approximatedType, it.declaresDefaultValue(),
it.isCrossinline, it.isNoinline, it.varargElementType, it.source
)
} else {
it
}
}
if (valueParameterApproximated) {
builder.setValueParameters(newParameters)
approximated = true
}
return approximated
}
private fun ReceiverParameterDescriptor.substituteTopLevelType(newType: KotlinType): ReceiverParameterDescriptor? {
val wrappedSubstitution = object : TypeSubstitution() {
override fun get(key: KotlinType): TypeProjection? = null
override fun prepareTopLevelType(topLevelType: KotlinType, position: Variance): KotlinType = newType
}
return substitute(TypeSubstitutor.create(wrappedSubstitution))
}
private fun TypeApproximator.approximate(type: UnwrappedType, toSuper: Boolean): KotlinType? {
if (type.arguments.isEmpty() && type.constructor.isDenotable) return null
return if (toSuper)
approximateToSuperType(type, TypeApproximatorConfiguration.PublicDeclaration)
else
approximateToSubType(type, TypeApproximatorConfiguration.PublicDeclaration)
}
/**
@@ -57,7 +124,7 @@ private fun <D : CallableMemberDescriptor> createFreeDescriptor(descriptor: D):
* when using reflection on that local variable at runtime.
* Only members used by [DescriptorSerializer.propertyProto] are implemented correctly in this property descriptor.
*/
fun createFreeFakeLocalPropertyDescriptor(descriptor: LocalVariableDescriptor): PropertyDescriptor {
fun createFreeFakeLocalPropertyDescriptor(descriptor: LocalVariableDescriptor, typeApproximator: TypeApproximator?): PropertyDescriptor {
val property = PropertyDescriptorImpl.create(
descriptor.containingDeclaration, descriptor.annotations, Modality.FINAL, descriptor.visibility, descriptor.isVar,
descriptor.name, CallableMemberDescriptor.Kind.DECLARATION, descriptor.source, false, descriptor.isConst,
@@ -83,5 +150,5 @@ fun createFreeFakeLocalPropertyDescriptor(descriptor: LocalVariableDescriptor):
}
)
return createFreeDescriptor(property)
return createFreeDescriptor(property, typeApproximator)
}
@@ -58,6 +58,7 @@ class JvmSerializerExtension @JvmOverloads constructor(
private val functionsWithInlineClassReturnTypesMangled = state.functionsWithInlineClassReturnTypesMangled
override val metadataVersion = state.metadataVersion
private val jvmDefaultMode = state.jvmDefaultMode
private val approximator = state.typeApproximator
override fun shouldUseTypeTable(): Boolean = useTypeTable
override fun shouldSerializeFunction(descriptor: FunctionDescriptor): Boolean {
@@ -137,7 +138,7 @@ class JvmSerializerExtension @JvmOverloads constructor(
val localVariables = CodegenBinding.getLocalDelegatedProperties(codegenBinding, classAsmType) ?: return
for (localVariable in localVariables) {
val propertyDescriptor = createFreeFakeLocalPropertyDescriptor(localVariable)
val propertyDescriptor = createFreeFakeLocalPropertyDescriptor(localVariable, approximator)
val serializer = DescriptorSerializer.createForLambda(this)
proto.addExtension(extension, serializer.propertyProto(propertyDescriptor)?.build() ?: continue)
}
@@ -46,6 +46,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind.*
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeApproximator
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method
import java.io.File
@@ -285,6 +286,12 @@ class GenerationState private constructor(
lateinit var irBasedMapAsmMethod: (FunctionDescriptor) -> Method
var mapInlineClass: (ClassDescriptor) -> Type = { descriptor -> typeMapper.mapType(descriptor.defaultType) }
val typeApproximator: TypeApproximator? =
if (languageVersionSettings.supportsFeature(LanguageFeature.NewInference))
TypeApproximator(module.builtIns)
else
null
init {
this.interceptedBuilderFactory = builderFactory
.wrapWith(