Approximate types for lambda literals before serialization
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
+2
-1
@@ -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(
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+9
-1
@@ -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
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+24
@@ -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 }
|
||||
}
|
||||
+88
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user