diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt index 0db54b26f73..83d1f4b3ae3 100644 --- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt @@ -110,7 +110,7 @@ class ClassCodegen private constructor( private val classOrigin = irClass.descriptorOrigin private val visitor = state.factory.newVisitor(classOrigin, type, irClass.fileParent.loadSourceFilesInfo()).apply { - val signature = typeMapper.mapClassSignature(irClass, type) + val signature = typeMapper.mapClassSignature(irClass, type, context.state.classBuilderMode.generateBodies) // Ensure that the backend only produces class names that would be valid in the frontend for JVM. if (context.state.classBuilderMode.generateBodies && signature.hasInvalidName()) { throw IllegalStateException("Generating class with invalid name '${type.className}': ${irClass.dump()}") diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt index ce8b65b5c92..fbe7ae54128 100644 --- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt @@ -218,7 +218,7 @@ private val KOTLIN_MARKER_INTERFACES: Map = run { kotlinMarkerInterfaces } -internal fun IrTypeMapper.mapClassSignature(irClass: IrClass, type: Type): JvmClassSignature { +internal fun IrTypeMapper.mapClassSignature(irClass: IrClass, type: Type, generateBodies: Boolean): JvmClassSignature { val sw = BothSignatureWriter(BothSignatureWriter.Mode.CLASS) writeFormalTypeParameters(irClass.typeParameters, sw) @@ -234,7 +234,11 @@ internal fun IrTypeMapper.mapClassSignature(irClass: IrClass, type: Type): JvmCl sw.writeSuperclassEnd() val kotlinMarkerInterfaces = LinkedHashSet() - if (irClass.superTypes.any { it.isSuspendFunction() || it.isKSuspendFunction() }) { + if (generateBodies && irClass.superTypes.any { it.isSuspendFunction() || it.isKSuspendFunction() }) { + // Do not generate this class in the kapt3 mode (generateBodies=false), because kapt3 transforms supertypes correctly in the + // "correctErrorTypes" mode only when the number of supertypes between PSI and bytecode is equal. Otherwise it tries to "correct" + // the FunctionN type and fails, because that type doesn't need an import in the Kotlin source (kotlin.FunctionN), but needs one + // in the Java source (kotlin.jvm.functions.FunctionN), and kapt3 doesn't perform any Kotlin->Java name lookup. kotlinMarkerInterfaces.add("kotlin/coroutines/jvm/internal/SuspendFunction") } diff --git a/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt b/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt index 00284a27225..99c6d7a8b01 100644 --- a/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt +++ b/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt @@ -539,6 +539,12 @@ class ClassFileToSourceStubConverter(val kaptContext: KaptContextForStubGenerati val sameSuperClassCount = (superClass == null) == (defaultSuperTypes.superClass == null) val sameSuperInterfaceCount = superInterfaces.size == defaultSuperTypes.interfaces.size + // Note: if the number of supertypes is different, it might mean either that one of them is unresolved, or that backend generated + // additional supertypes which were not present in the PSI. + // In the former case, the subsequent code behaves as expected, trying to recover the types from the PSI. + // In the latter case, ideally we shouldn't do anything, but most of the time invoking error type correction is harmless because + // it will be a no-op. However, it might lead to problems for non-trivial types such as `kotlin.FunctionN` which are mapped to + // `kotlin.jvm.functions.FunctionN`, because the Java source requires a new import, unlike the Kotlin source. if (sameSuperClassCount && sameSuperInterfaceCount) { return defaultSuperTypes } diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/suspendFunctionSupertype.kt b/plugins/kapt3/kapt3-compiler/testData/converter/suspendFunctionSupertype.kt new file mode 100644 index 00000000000..f097066fc56 --- /dev/null +++ b/plugins/kapt3/kapt3-compiler/testData/converter/suspendFunctionSupertype.kt @@ -0,0 +1,3 @@ +// CORRECT_ERROR_TYPES + +interface SomeInterface : suspend (List, Boolean) -> List diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/suspendFunctionSupertype.txt b/plugins/kapt3/kapt3-compiler/testData/converter/suspendFunctionSupertype.txt new file mode 100644 index 00000000000..c5e0f2ccddd --- /dev/null +++ b/plugins/kapt3/kapt3-compiler/testData/converter/suspendFunctionSupertype.txt @@ -0,0 +1,3 @@ +@kotlin.Metadata() +public abstract interface SomeInterface extends kotlin.jvm.functions.Function3, java.lang.Boolean, kotlin.coroutines.Continuation>, java.lang.Object> { +} diff --git a/plugins/kapt3/kapt3-compiler/tests-gen/org/jetbrains/kotlin/kapt3/test/runners/ClassFileToSourceStubConverterTestGenerated.java b/plugins/kapt3/kapt3-compiler/tests-gen/org/jetbrains/kotlin/kapt3/test/runners/ClassFileToSourceStubConverterTestGenerated.java index d8a63471848..79e80926374 100644 --- a/plugins/kapt3/kapt3-compiler/tests-gen/org/jetbrains/kotlin/kapt3/test/runners/ClassFileToSourceStubConverterTestGenerated.java +++ b/plugins/kapt3/kapt3-compiler/tests-gen/org/jetbrains/kotlin/kapt3/test/runners/ClassFileToSourceStubConverterTestGenerated.java @@ -619,6 +619,12 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi runTest("plugins/kapt3/kapt3-compiler/testData/converter/suspendErrorTypes.kt"); } + @Test + @TestMetadata("suspendFunctionSupertype.kt") + public void testSuspendFunctionSupertype() throws Exception { + runTest("plugins/kapt3/kapt3-compiler/testData/converter/suspendFunctionSupertype.kt"); + } + @Test @TestMetadata("topLevel.kt") public void testTopLevel() throws Exception { diff --git a/plugins/kapt3/kapt3-compiler/tests-gen/org/jetbrains/kotlin/kapt3/test/runners/IrClassFileToSourceStubConverterTestGenerated.java b/plugins/kapt3/kapt3-compiler/tests-gen/org/jetbrains/kotlin/kapt3/test/runners/IrClassFileToSourceStubConverterTestGenerated.java index 1b90464f7e0..7d696c84a60 100644 --- a/plugins/kapt3/kapt3-compiler/tests-gen/org/jetbrains/kotlin/kapt3/test/runners/IrClassFileToSourceStubConverterTestGenerated.java +++ b/plugins/kapt3/kapt3-compiler/tests-gen/org/jetbrains/kotlin/kapt3/test/runners/IrClassFileToSourceStubConverterTestGenerated.java @@ -619,6 +619,12 @@ public class IrClassFileToSourceStubConverterTestGenerated extends AbstractIrCla runTest("plugins/kapt3/kapt3-compiler/testData/converter/suspendErrorTypes.kt"); } + @Test + @TestMetadata("suspendFunctionSupertype.kt") + public void testSuspendFunctionSupertype() throws Exception { + runTest("plugins/kapt3/kapt3-compiler/testData/converter/suspendFunctionSupertype.kt"); + } + @Test @TestMetadata("topLevel.kt") public void testTopLevel() throws Exception {