diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java index 7e2dd41992a..1dfcb4a102b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java @@ -259,7 +259,9 @@ public class ClosureCodegen extends MemberCodegen { 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 = diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt index 2bc8e6f0cd4..678ac129a65 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt @@ -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) } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/fakeDescriptorsForReferences.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/fakeDescriptorsForReferences.kt index 0de7769d4b5..79ca18b5e99 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/fakeDescriptorsForReferences.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/fakeDescriptorsForReferences.kt @@ -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 createFreeDescriptor(descriptor: D): D { +private fun createFreeDescriptor(descriptor: D, typeApproximator: TypeApproximator?): D { @Suppress("UNCHECKED_CAST") val builder = descriptor.newCopyBuilder() as CallableMemberDescriptor.CopyBuilder @@ -49,7 +47,76 @@ private fun 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 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) } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt index 1819404ee34..bc64c14b0e2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt @@ -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) } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index 6133d05879c..acc54819557 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -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( diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/DescriptorBasedClassCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/DescriptorBasedClassCodegen.kt index 09a6de41ba4..4094f525c11 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/DescriptorBasedClassCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/DescriptorBasedClassCodegen.kt @@ -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) { diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/CallableMemberDescriptor.java b/core/descriptors/src/org/jetbrains/kotlin/descriptors/CallableMemberDescriptor.java index 075616f3b45..7813b3a3f49 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/CallableMemberDescriptor.java +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/CallableMemberDescriptor.java @@ -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 setPreserveSourceElement(); + @NotNull + CopyBuilder setReturnType(@NotNull KotlinType type); + @Nullable D build(); } diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/FunctionDescriptor.java b/core/descriptors/src/org/jetbrains/kotlin/descriptors/FunctionDescriptor.java index 42d426c12b8..a81045dc454 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/FunctionDescriptor.java +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/FunctionDescriptor.java @@ -105,6 +105,7 @@ public interface FunctionDescriptor extends CallableMemberDescriptor { CopyBuilder setTypeParameters(@NotNull List parameters); @NotNull + @Override CopyBuilder setReturnType(@NotNull KotlinType type); @NotNull diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/PropertyDescriptorImpl.java b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/PropertyDescriptorImpl.java index 83370bbd695..680ec8edd61 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/PropertyDescriptorImpl.java +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/PropertyDescriptorImpl.java @@ -264,6 +264,7 @@ public class PropertyDescriptorImpl extends VariableDescriptorWithInitializerImp private ReceiverParameterDescriptor dispatchReceiverParameter = PropertyDescriptorImpl.this.dispatchReceiverParameter; private List 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 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 diff --git a/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestGenerated.java b/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestGenerated.java index a0df838001c..772224c1e5e 100644 --- a/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestGenerated.java +++ b/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestGenerated.java @@ -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"); diff --git a/libraries/tools/kotlinp/testData/IntersectionTypeInLambdaLiteralAndDelegatedProperty.kt b/libraries/tools/kotlinp/testData/IntersectionTypeInLambdaLiteralAndDelegatedProperty.kt new file mode 100644 index 00000000000..90a2724f8d8 --- /dev/null +++ b/libraries/tools/kotlinp/testData/IntersectionTypeInLambdaLiteralAndDelegatedProperty.kt @@ -0,0 +1,24 @@ +interface A +interface B +class Inv(e: T) + +fun intersection(x: Inv, y: Inv): S = TODO() + +fun use(k: K, f: K.(K) -> K) {} +fun useNested(k: K, f: Inv.(Inv) -> Inv) {} + +fun createDelegate(f: () -> T): Delegate = Delegate() + +class Delegate { + 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, b: Inv) { + val intersectionType = intersection(a, b) + + use(intersectionType) { intersectionType } + useNested(intersectionType) { Inv(intersectionType) } + + var d by createDelegate { intersectionType } +} diff --git a/libraries/tools/kotlinp/testData/IntersectionTypeInLambdaLiteralAndDelegatedProperty.txt b/libraries/tools/kotlinp/testData/IntersectionTypeInLambdaLiteralAndDelegatedProperty.txt new file mode 100644 index 00000000000..ed26a38b2cf --- /dev/null +++ b/libraries/tools/kotlinp/testData/IntersectionTypeInLambdaLiteralAndDelegatedProperty.txt @@ -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 : kotlin/Any { + + // signature: ()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 createDelegate(f: kotlin/Function0): Delegate + + // signature: intersection(LInv;LInv;)Ljava/lang/Object; + public final fun intersection(x: Inv, y: Inv): T#0 + + // signature: test(LInv;LInv;)V + public final fun test(a: Inv, b: Inv): kotlin/Unit + + // signature: use(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V + public final fun use(k: T#0, f: @kotlin/ExtensionFunctionType kotlin/Function2): kotlin/Unit + + // signature: useNested(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V + public final fun useNested(k: T#0, f: @kotlin/ExtensionFunctionType kotlin/Function2, Inv, Inv>): 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.(it: kotlin/Nothing): kotlin/Any +} +// IntersectionTypeInLambdaLiteralAndDelegatedPropertyKt$test$2.class +// ------------------------------------------ +lambda { + + // signature: invoke(LInv;LInv;)LInv; + local final fun kotlin/Nothing.(it: kotlin/Nothing): Inv +} +// IntersectionTypeInLambdaLiteralAndDelegatedPropertyKt$test$d$2.class +// ------------------------------------------ +lambda { + + // signature: invoke()Ljava/lang/Object; + local final fun (): kotlin/Any +} +// Inv.class +// ------------------------------------------ +public final class Inv : kotlin/Any { + + // signature: (Ljava/lang/Object;)V + public /* primary */ constructor(e: T#0) + + // module name: test-module +} +// META-INF/test-module.kotlin_module +// ------------------------------------------ +module { + package { + IntersectionTypeInLambdaLiteralAndDelegatedPropertyKt + } +}