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(
@@ -87,7 +87,7 @@ class DescriptorBasedClassCodegen internal constructor(
}
}
is MetadataSource.Function -> {
val fakeDescriptor = createFreeFakeLambdaDescriptor(metadata.descriptor)
val fakeDescriptor = createFreeFakeLambdaDescriptor(metadata.descriptor, state.typeApproximator)
val functionProto = serializer!!.functionProto(fakeDescriptor)?.build()
writeKotlinMetadata(visitor, state, KotlinClassHeader.Kind.SYNTHETIC_CLASS, extraFlags) {
if (functionProto != null) {
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.descriptors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeSubstitution;
import java.util.Collection;
@@ -93,6 +94,9 @@ public interface CallableMemberDescriptor extends CallableDescriptor, MemberDesc
@NotNull
CopyBuilder<D> setPreserveSourceElement();
@NotNull
CopyBuilder<D> setReturnType(@NotNull KotlinType type);
@Nullable
D build();
}
@@ -105,6 +105,7 @@ public interface FunctionDescriptor extends CallableMemberDescriptor {
CopyBuilder<D> setTypeParameters(@NotNull List<TypeParameterDescriptor> parameters);
@NotNull
@Override
CopyBuilder<D> setReturnType(@NotNull KotlinType type);
@NotNull
@@ -264,6 +264,7 @@ public class PropertyDescriptorImpl extends VariableDescriptorWithInitializerImp
private ReceiverParameterDescriptor dispatchReceiverParameter = PropertyDescriptorImpl.this.dispatchReceiverParameter;
private List<TypeParameterDescriptor> newTypeParameters = null;
private Name name = getName();
private KotlinType returnType = getType();
@NotNull
@Override
@@ -286,6 +287,13 @@ public class PropertyDescriptorImpl extends VariableDescriptorWithInitializerImp
return this;
}
@NotNull
@Override
public CopyBuilder<PropertyDescriptor> setReturnType(@NotNull KotlinType type) {
returnType = type;
return this;
}
@NotNull
@Override
public CopyConfiguration setModality(@NotNull Modality modality) {
@@ -386,7 +394,7 @@ public class PropertyDescriptorImpl extends VariableDescriptorWithInitializerImp
originalTypeParameters, copyConfiguration.substitution, substitutedDescriptor, substitutedTypeParameters
);
KotlinType originalOutType = getType();
KotlinType originalOutType = copyConfiguration.returnType;
KotlinType outType = substitutor.substitute(originalOutType, Variance.OUT_VARIANCE);
if (outType == null) {
return null; // TODO : tell the user that the property was projected out
@@ -43,6 +43,11 @@ public class KotlinpTestGenerated extends AbstractKotlinpTest {
runTest("libraries/tools/kotlinp/testData/FunInterface.kt");
}
@TestMetadata("IntersectionTypeInLambdaLiteralAndDelegatedProperty.kt")
public void testIntersectionTypeInLambdaLiteralAndDelegatedProperty() throws Exception {
runTest("libraries/tools/kotlinp/testData/IntersectionTypeInLambdaLiteralAndDelegatedProperty.kt");
}
@TestMetadata("Lambda.kt")
public void testLambda() throws Exception {
runTest("libraries/tools/kotlinp/testData/Lambda.kt");
@@ -0,0 +1,24 @@
interface A
interface B
class Inv<T>(e: T)
fun <S> intersection(x: Inv<in S>, y: Inv<in S>): S = TODO()
fun <K> use(k: K, f: K.(K) -> K) {}
fun <K> useNested(k: K, f: Inv<K>.(Inv<K>) -> Inv<K>) {}
fun <T> createDelegate(f: () -> T): Delegate<T> = Delegate()
class Delegate<T> {
operator fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): T = TODO()
operator fun setValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>, value: T) {}
}
fun test(a: Inv<A>, b: Inv<B>) {
val intersectionType = intersection(a, b)
use(intersectionType) { intersectionType }
useNested(intersectionType) { Inv(intersectionType) }
var d by createDelegate { intersectionType }
}
@@ -0,0 +1,88 @@
// A.class
// ------------------------------------------
public abstract interface A : kotlin/Any {
// module name: test-module
}
// B.class
// ------------------------------------------
public abstract interface B : kotlin/Any {
// module name: test-module
}
// Delegate.class
// ------------------------------------------
public final class Delegate<T#0 /* T */> : kotlin/Any {
// signature: <init>()V
public /* primary */ constructor()
// signature: getValue(Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object;
public final operator fun getValue(thisRef: kotlin/Any?, property: kotlin/reflect/KProperty<*>): T#0
// signature: setValue(Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V
public final operator fun setValue(thisRef: kotlin/Any?, property: kotlin/reflect/KProperty<*>, value: T#0): kotlin/Unit
// module name: test-module
}
// IntersectionTypeInLambdaLiteralAndDelegatedPropertyKt.class
// ------------------------------------------
package {
// signature: createDelegate(Lkotlin/jvm/functions/Function0;)LDelegate;
public final fun <T#0 /* T */> createDelegate(f: kotlin/Function0<T#0>): Delegate<T#0>
// signature: intersection(LInv;LInv;)Ljava/lang/Object;
public final fun <T#0 /* S */> intersection(x: Inv<in T#0>, y: Inv<in T#0>): T#0
// signature: test(LInv;LInv;)V
public final fun test(a: Inv<A>, b: Inv<B>): kotlin/Unit
// signature: use(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V
public final fun <T#0 /* K */> use(k: T#0, f: @kotlin/ExtensionFunctionType kotlin/Function2<T#0, T#0, T#0>): kotlin/Unit
// signature: useNested(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V
public final fun <T#0 /* K */> useNested(k: T#0, f: @kotlin/ExtensionFunctionType kotlin/Function2<Inv<T#0>, Inv<T#0>, Inv<T#0>>): kotlin/Unit
// local delegated property #0
// local final /* delegated */ var d: kotlin/Any
// local final get
// local final set
}
// IntersectionTypeInLambdaLiteralAndDelegatedPropertyKt$test$1.class
// ------------------------------------------
lambda {
// signature: invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
local final fun kotlin/Nothing.<anonymous>(it: kotlin/Nothing): kotlin/Any
}
// IntersectionTypeInLambdaLiteralAndDelegatedPropertyKt$test$2.class
// ------------------------------------------
lambda {
// signature: invoke(LInv;LInv;)LInv;
local final fun kotlin/Nothing.<anonymous>(it: kotlin/Nothing): Inv<out kotlin/Any>
}
// IntersectionTypeInLambdaLiteralAndDelegatedPropertyKt$test$d$2.class
// ------------------------------------------
lambda {
// signature: invoke()Ljava/lang/Object;
local final fun <anonymous>(): kotlin/Any
}
// Inv.class
// ------------------------------------------
public final class Inv<T#0 /* T */> : kotlin/Any {
// signature: <init>(Ljava/lang/Object;)V
public /* primary */ constructor(e: T#0)
// module name: test-module
}
// META-INF/test-module.kotlin_module
// ------------------------------------------
module {
package <root> {
IntersectionTypeInLambdaLiteralAndDelegatedPropertyKt
}
}