From 089928ba2b2dabe539a6ffe3e62d15d61a8eb623 Mon Sep 17 00:00:00 2001 From: Mads Ager Date: Wed, 10 May 2023 13:39:51 +0200 Subject: [PATCH] [K2] Support legacy serialization for custom function type extensions. For Compose libraries, it is important that libraries built with K2 in version 2.0 can be read and used by a version 1.9 K1 compiler. That is not possible if custom function types are in the metadata. ^KT-58456 Fixed --- .../kotlin/test/LoadedMetadataDumpHandler.kt | 11 +++++- .../fir/serialization/FirElementSerializer.kt | 12 +++++- .../builtins/functions/FunctionTypeKind.kt | 16 ++++++++ .../kotlin/generators/tests/GenerateTests.kt | 5 +++ plugins/fir-plugin-prototype/build.gradle.kts | 1 + ...ComposableLikeFunctionTypeKindExtension.kt | 7 ++++ .../simple-lang-ver-2.1.fir.k2.txt | 1 + .../firLoadK2Compiled/simple-lang-ver-2.1.kt | 7 ++++ .../firLoadK2Compiled/simple.fir.k2.txt | 1 + .../testData/firLoadK2Compiled/simple.kt | 8 ++++ ...CompiledKotlinWithPluginTestGenerated.java | 39 +++++++++++++++++++ .../plugin/runners/PluginPrototypeTests.kt | 8 ++++ 12 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple-lang-ver-2.1.fir.k2.txt create mode 100644 plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple-lang-ver-2.1.kt create mode 100644 plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple.fir.k2.txt create mode 100644 plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple.kt create mode 100644 plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirLoadK2CompiledKotlinWithPluginTestGenerated.java diff --git a/compiler/fir/analysis-tests/test/org/jetbrains/kotlin/test/LoadedMetadataDumpHandler.kt b/compiler/fir/analysis-tests/test/org/jetbrains/kotlin/test/LoadedMetadataDumpHandler.kt index faa7c9e8c9f..aa0c9d94b9f 100644 --- a/compiler/fir/analysis-tests/test/org/jetbrains/kotlin/test/LoadedMetadataDumpHandler.kt +++ b/compiler/fir/analysis-tests/test/org/jetbrains/kotlin/test/LoadedMetadataDumpHandler.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.vfs.StandardFileSystems import com.intellij.openapi.vfs.VirtualFileManager import org.jetbrains.kotlin.cli.common.prepareJvmSessions import org.jetbrains.kotlin.cli.jvm.compiler.VfsBasedProjectEnvironment +import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl import org.jetbrains.kotlin.fir.BinaryModuleData import org.jetbrains.kotlin.fir.DependencyListForCliModule @@ -26,7 +27,9 @@ import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices import org.jetbrains.kotlin.test.backend.handlers.JvmBinaryArtifactHandler +import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives.LANGUAGE_VERSION import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives +import org.jetbrains.kotlin.test.directives.model.singleOrZeroValue import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact import org.jetbrains.kotlin.test.model.* @@ -42,11 +45,17 @@ class LoadedMetadataDumpHandler(testServices: TestServices) : JvmBinaryArtifactH override fun processModule(module: TestModule, info: BinaryArtifacts.Jvm) { if (testServices.loadedMetadataSuppressionDirective in module.directives) return + val languageVersion = module.directives.singleOrZeroValue(LANGUAGE_VERSION) + val languageVersionSettings = if (languageVersion != null) { + LanguageVersionSettingsImpl(languageVersion, ApiVersion.createByLanguageVersion(languageVersion)) + } else { + LanguageVersionSettingsImpl.DEFAULT + } val emptyModule = TestModule( name = "empty", JvmPlatforms.defaultJvmPlatform, TargetBackend.JVM_IR, FrontendKinds.FIR, BackendKinds.IrBackend, ArtifactKinds.Jvm, files = emptyList(), allDependencies = listOf(DependencyDescription(module.name, DependencyKind.Binary, DependencyRelation.RegularDependency)), - RegisteredDirectives.Empty, LanguageVersionSettingsImpl.DEFAULT + RegisteredDirectives.Empty, languageVersionSettings ) val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(emptyModule) val environment = VfsBasedProjectEnvironment( diff --git a/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirElementSerializer.kt b/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirElementSerializer.kt index 677d644b2ad..b472df32ed3 100644 --- a/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirElementSerializer.kt +++ b/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirElementSerializer.kt @@ -7,7 +7,9 @@ package org.jetbrains.kotlin.fir.serialization import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind +import org.jetbrains.kotlin.builtins.functions.isBuiltin import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageVersion import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.constant.EnumValue import org.jetbrains.kotlin.constant.IntValue @@ -823,7 +825,8 @@ class FirElementSerializer private constructor( return lowerBound } is ConeClassLikeType -> { - if (type.functionTypeKind(session) == FunctionTypeKind.SuspendFunction) { + val functionTypeKind = type.functionTypeKind(session) + if (functionTypeKind == FunctionTypeKind.SuspendFunction) { val runtimeFunctionType = type.suspendFunctionTypeToFunctionTypeWithContinuation( session, StandardClassIds.Continuation ) @@ -831,6 +834,13 @@ class FirElementSerializer private constructor( functionType.flags = Flags.getTypeFlags(true, false) return functionType } + if (functionTypeKind?.isBuiltin == false) { + val legacySerializationUntil = + LanguageVersion.fromVersionString(functionTypeKind.serializeAsFunctionWithAnnotationUntil) + if (legacySerializationUntil != null && languageVersionSettings.languageVersion < legacySerializationUntil) { + return typeProto(type.customFunctionTypeToSimpleFunctionType(session)) + } + } fillFromPossiblyInnerType(builder, type) if (type.hasContextReceivers) { typeAnnotations.addIfNotNull( diff --git a/core/compiler.common/src/org/jetbrains/kotlin/builtins/functions/FunctionTypeKind.kt b/core/compiler.common/src/org/jetbrains/kotlin/builtins/functions/FunctionTypeKind.kt index ad9108e8ed5..780eef0e730 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/builtins/functions/FunctionTypeKind.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/builtins/functions/FunctionTypeKind.kt @@ -63,6 +63,22 @@ abstract class FunctionTypeKind internal constructor( open val prefixForTypeRender: String? get() = null + + /** + * Specifies the first language version for which to serialize custom function types + * into kotlin metadata. Until that version, custom function types will be serialized + * with the legacy scheme: as a FunctionN/KFunctionN with the annotation used for the + * custom function type. + * + * If no version is specified, custom function types are serialized to kotlin metadata. + * + * Serialization using the legacy format allows libraries compiled with K2 with a + * K2 plugin that uses custom function types to be used by clients using a K1 compiler + * with a K1 compiler plugin that understands the custom function types. + */ + open val serializeAsFunctionWithAnnotationUntil: String? + get() = null + /** * @return corresponding non-reflect kind for reflect kind * @return [this] if [isReflectType] is false diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 9cc6e3576ba..cf1ab18c396 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.assignment.plugin.AbstractFirLightTreeBlackBoxCodege import org.jetbrains.kotlin.assignment.plugin.AbstractFirPsiAssignmentPluginDiagnosticTest import org.jetbrains.kotlin.assignment.plugin.AbstractIrBlackBoxCodegenTestAssignmentPlugin import org.jetbrains.kotlin.fir.plugin.runners.AbstractFirLightTreePluginBlackBoxCodegenTest +import org.jetbrains.kotlin.fir.plugin.runners.AbstractFirLoadK2CompiledKotlinWithPluginTest import org.jetbrains.kotlin.fir.plugin.runners.AbstractFirPsiPluginDiagnosticTest import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5 import org.jetbrains.kotlin.generators.impl.generateTestGroupSuite @@ -262,6 +263,10 @@ fun main(args: Array) { testClass { model("box") } + + testClass { + model("firLoadK2Compiled") + } } testGroup( diff --git a/plugins/fir-plugin-prototype/build.gradle.kts b/plugins/fir-plugin-prototype/build.gradle.kts index 11ea7bb6859..02ee15f0481 100644 --- a/plugins/fir-plugin-prototype/build.gradle.kts +++ b/plugins/fir-plugin-prototype/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { testApi(projectTests(":compiler:tests-common-new")) testApi(projectTests(":compiler:test-infrastructure")) testApi(projectTests(":compiler:test-infrastructure-utils")) + testApi(projectTests(":compiler:fir:analysis-tests")) testApi(project(":compiler:fir:checkers")) testApi(project(":compiler:fir:checkers:checkers.jvm")) testApi(project(":compiler:fir:checkers:checkers.js")) diff --git a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/types/ComposableLikeFunctionTypeKindExtension.kt b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/types/ComposableLikeFunctionTypeKindExtension.kt index b4322ca514e..40fb1439725 100644 --- a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/types/ComposableLikeFunctionTypeKindExtension.kt +++ b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/types/ComposableLikeFunctionTypeKindExtension.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.plugin.types import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind +import org.jetbrains.kotlin.config.LanguageVersion import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.extensions.FirFunctionTypeKindExtension import org.jetbrains.kotlin.fir.plugin.fqn @@ -31,6 +32,9 @@ object ComposableFunction : FunctionTypeKind( override val prefixForTypeRender: String get() = "@MyComposable" + override val serializeAsFunctionWithAnnotationUntil: String + get() = LanguageVersion.KOTLIN_2_1.versionString + override fun reflectKind(): FunctionTypeKind = KComposableFunction } @@ -40,5 +44,8 @@ object KComposableFunction : FunctionTypeKind( MY_COMPOSABLE_ANNOTATION_CLASS_ID, isReflectType = true ) { + override val serializeAsFunctionWithAnnotationUntil: String + get() = LanguageVersion.KOTLIN_2_1.versionString + override fun nonReflectKind(): FunctionTypeKind = ComposableFunction } diff --git a/plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple-lang-ver-2.1.fir.k2.txt b/plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple-lang-ver-2.1.fir.k2.txt new file mode 100644 index 00000000000..0c3bee80900 --- /dev/null +++ b/plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple-lang-ver-2.1.fir.k2.txt @@ -0,0 +1 @@ +public final fun runComposable(block: R|@R|org/jetbrains/kotlin/fir/plugin/MyComposable|() some/MyComposableFunction0|): R|kotlin/Unit| diff --git a/plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple-lang-ver-2.1.kt b/plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple-lang-ver-2.1.kt new file mode 100644 index 00000000000..4fce8065553 --- /dev/null +++ b/plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple-lang-ver-2.1.kt @@ -0,0 +1,7 @@ +// API_VERSION: 2.0 +// LANGUAGE_VERSION: 2.1 +package test + +import org.jetbrains.kotlin.fir.plugin.MyComposable + +fun runComposable(block: @MyComposable () -> Unit) {} diff --git a/plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple.fir.k2.txt b/plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple.fir.k2.txt new file mode 100644 index 00000000000..1af899c7877 --- /dev/null +++ b/plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple.fir.k2.txt @@ -0,0 +1 @@ +public final fun runComposable(block: R|@R|org/jetbrains/kotlin/fir/plugin/MyComposable|() () -> kotlin/Unit|): R|kotlin/Unit| diff --git a/plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple.kt b/plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple.kt new file mode 100644 index 00000000000..f5982ca1d07 --- /dev/null +++ b/plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple.kt @@ -0,0 +1,8 @@ +// API_VERSION: 2.0 +// LANGUAGE_VERSION: 2.0 +package test + +import org.jetbrains.kotlin.fir.plugin.MyComposable + +fun runComposable(block: @MyComposable () -> Unit) {} + diff --git a/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirLoadK2CompiledKotlinWithPluginTestGenerated.java b/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirLoadK2CompiledKotlinWithPluginTestGenerated.java new file mode 100644 index 00000000000..b8609ce115e --- /dev/null +++ b/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirLoadK2CompiledKotlinWithPluginTestGenerated.java @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.plugin.runners; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateTestsKt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("plugins/fir-plugin-prototype/testData/firLoadK2Compiled") +@TestDataPath("$PROJECT_ROOT") +public class FirLoadK2CompiledKotlinWithPluginTestGenerated extends AbstractFirLoadK2CompiledKotlinWithPluginTest { + @Test + public void testAllFilesPresentInFirLoadK2Compiled() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/fir-plugin-prototype/testData/firLoadK2Compiled"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple.kt"); + } + + @Test + @TestMetadata("simple-lang-ver-2.1.kt") + public void testSimple_lang_ver_2_1() throws Exception { + runTest("plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple-lang-ver-2.1.kt"); + } +} diff --git a/plugins/fir-plugin-prototype/tests/org/jetbrains/kotlin/fir/plugin/runners/PluginPrototypeTests.kt b/plugins/fir-plugin-prototype/tests/org/jetbrains/kotlin/fir/plugin/runners/PluginPrototypeTests.kt index 7eb730926d7..fa4f61cd7ff 100644 --- a/plugins/fir-plugin-prototype/tests/org/jetbrains/kotlin/fir/plugin/runners/PluginPrototypeTests.kt +++ b/plugins/fir-plugin-prototype/tests/org/jetbrains/kotlin/fir/plugin/runners/PluginPrototypeTests.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.plugin.services.PluginAnnotationsProvider import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.ENABLE_PLUGIN_PHASES import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.FIR_DUMP +import org.jetbrains.kotlin.test.runners.AbstractFirLoadK2CompiledKotlin import org.jetbrains.kotlin.test.runners.AbstractFirPsiDiagnosticTest import org.jetbrains.kotlin.test.runners.codegen.AbstractFirLightTreeBlackBoxCodegenTest import org.jetbrains.kotlin.test.runners.enableLazyResolvePhaseChecking @@ -28,6 +29,13 @@ abstract class AbstractFirPsiPluginDiagnosticTest : AbstractFirPsiDiagnosticTest } } +open class AbstractFirLoadK2CompiledKotlinWithPluginTest : AbstractFirLoadK2CompiledKotlin() { + override fun configure(builder: TestConfigurationBuilder) { + super.configure(builder) + builder.commonFirWithPluginFrontendConfiguration() + } +} + fun TestConfigurationBuilder.commonFirWithPluginFrontendConfiguration() { enableLazyResolvePhaseChecking()