diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java index 225df0cf39b..ee31db560c0 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java @@ -63,6 +63,7 @@ import static org.jetbrains.kotlin.codegen.ExpressionCodegen.generateClassLitera import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConst; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.CLOSURE; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.asmTypeForAnonymousClass; +import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION; import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*; import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN; import static org.jetbrains.org.objectweb.asm.Opcodes.*; @@ -228,10 +229,15 @@ public class ClosureCodegen extends MemberCodegen { @Override protected void generateKotlinMetadataAnnotation() { + FunctionDescriptor freeLambdaDescriptor = createFreeLambdaDescriptor(funDescriptor); + Method method = v.getSerializationBindings().get(METHOD_FOR_FUNCTION, funDescriptor); + assert method != null : "No method for " + funDescriptor; + v.getSerializationBindings().put(METHOD_FOR_FUNCTION, freeLambdaDescriptor, method); + final DescriptorSerializer serializer = DescriptorSerializer.createForLambda(new JvmSerializerExtension(v.getSerializationBindings(), state)); - final ProtoBuf.Function functionProto = serializer.functionProto(funDescriptor).build(); + final ProtoBuf.Function functionProto = serializer.functionProto(freeLambdaDescriptor).build(); WriteAnnotationUtilKt.writeKotlinMetadata(v, KotlinClassHeader.Kind.SYNTHETIC_CLASS, new Function1() { @Override @@ -242,6 +248,30 @@ public class ClosureCodegen extends MemberCodegen { }); } + /** + * 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. + */ + @NotNull + private static FunctionDescriptor createFreeLambdaDescriptor(@NotNull FunctionDescriptor descriptor) { + FunctionDescriptor.CopyBuilder builder = descriptor.newCopyBuilder(); + List typeParameters = new ArrayList(0); + builder.setTypeParameters(typeParameters); + + DeclarationDescriptor container = descriptor.getContainingDeclaration(); + while (container != null) { + if (container instanceof ClassDescriptor) { + typeParameters.addAll(((ClassDescriptor) container).getDeclaredTypeParameters()); + } + else if (container instanceof CallableDescriptor && !(container instanceof ConstructorDescriptor)) { + typeParameters.addAll(((CallableDescriptor) container).getTypeParameters()); + } + container = container.getContainingDeclaration(); + } + + return typeParameters.isEmpty() ? descriptor : builder.build(); + } + @Override protected void done() { writeOuterClassAndEnclosingMethod(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java index 4644e40ccbc..d144f10d240 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java @@ -273,6 +273,11 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes LazyClassDescriptor.this, null, Annotations.Companion.getEMPTY(), Name.special(""), CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE ) { + { + initialize(null, null, Collections.emptyList(), Collections.emptyList(), + null, Modality.FINAL, Visibilities.PRIVATE); + } + @NotNull @Override protected FunctionDescriptorImpl createSubstitutedCopy( diff --git a/compiler/testData/codegen/box/functions/functionNtoStringGeneric.kt b/compiler/testData/codegen/box/functions/functionNtoStringGeneric.kt new file mode 100644 index 00000000000..0ce31a33df0 --- /dev/null +++ b/compiler/testData/codegen/box/functions/functionNtoStringGeneric.kt @@ -0,0 +1,23 @@ +// WITH_REFLECT + +import kotlin.test.assertEquals + +fun bar(): String { + return { t: T -> t }.toString() +} + +class Baz { + fun baz(v: V): String { + return (fun(t: List): V = v).toString() + } +} + +open class Foo>(val lambda: (T) -> U) +class Bar : Foo>({ listOf(it) }) + +fun box(): String { + assertEquals("(T) -> T", bar()) + assertEquals("(kotlin.collections.List) -> V", Baz().baz("")) + assertEquals("(T) -> kotlin.collections.List", Bar().lambda.toString()) + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index d32a9e8bd34..86e5c3b2609 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -6469,6 +6469,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("functionNtoStringGeneric.kt") + public void testFunctionNtoStringGeneric() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/functionNtoStringGeneric.kt"); + doTest(fileName); + } + @TestMetadata("functionNtoStringNoReflect.kt") public void testFunctionNtoStringNoReflect() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/functionNtoStringNoReflect.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/FunctionDescriptorImpl.java b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/FunctionDescriptorImpl.java index 81435e03cee..44e3a0a4660 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/FunctionDescriptorImpl.java +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/FunctionDescriptorImpl.java @@ -563,21 +563,14 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo configuration.newOwner, configuration.original, configuration.kind, configuration.name, resultAnnotations, getSourceToUseForCopy(configuration.preserveSourceElement, configuration.original, configuration.sourceElement)); - List substitutedTypeParameters; - final TypeSubstitutor substitutor; + List unsubstitutedTypeParameters = + configuration.newTypeParameters == null ? getTypeParameters() : configuration.newTypeParameters; - if (configuration.newTypeParameters == null) { - List originalTypeParameters = getTypeParameters(); - substitutedTypeParameters = new ArrayList(originalTypeParameters.size()); - substitutor = DescriptorSubstitutor.substituteTypeParameters( - originalTypeParameters, configuration.substitution, substitutedDescriptor, substitutedTypeParameters - ); - } - else { - // They should be already substituted - substitutedTypeParameters = configuration.newTypeParameters; - substitutor = configuration.substitution.buildSubstitutor(); - } + List substitutedTypeParameters = + new ArrayList(unsubstitutedTypeParameters.size()); + final TypeSubstitutor substitutor = DescriptorSubstitutor.substituteTypeParameters( + unsubstitutedTypeParameters, configuration.substitution, substitutedDescriptor, substitutedTypeParameters + ); KotlinType substitutedReceiverParameterType = null; if (configuration.newExtensionReceiverParameterType != null) { diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/reflectLambda.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/reflectLambda.kt index 78b3d7b259f..c24f483d36b 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/reflectLambda.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/reflectLambda.kt @@ -45,8 +45,8 @@ fun Function.reflect(): KFunction? { val proto = ProtoBuf.Function.parseFrom(input, JvmProtoBufUtil.EXTENSION_REGISTRY) val moduleData = javaClass.getOrCreateModule() val context = DeserializationContext( - moduleData.deserialization, nameResolver, moduleData.module, - typeTable = TypeTable(proto.typeTable), containerSource = null, parentTypeDeserializer = null, typeParameters = listOf() + moduleData.deserialization, nameResolver, moduleData.module, TypeTable(proto.typeTable), + containerSource = null, parentTypeDeserializer = null, typeParameters = proto.typeParameterList ) val descriptor = MemberDeserializer(context).loadFunction(proto) @Suppress("UNCHECKED_CAST")