JVM IR: retain optional annotations as package private classes
Similarly to how it's done in the old backend (see PackageCodegenImpl.generateClassesAndObjectsInFile and AnnotationCodegen.genAnnotation)
This commit is contained in:
+2
-2
@@ -15,9 +15,9 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
/**
|
||||
* This pass removes all declarations with `isExpect == true`.
|
||||
*/
|
||||
class ExpectDeclarationsRemoveLowering(val context: BackendContext) : FileLoweringPass {
|
||||
class ExpectDeclarationsRemoveLowering(val context: BackendContext, keepOptionalAnnotations: Boolean = false) : FileLoweringPass {
|
||||
|
||||
val visitor = ExpectDeclarationRemover(context.ir.symbols.externalSymbolTable, true)
|
||||
val visitor = ExpectDeclarationRemover(context.ir.symbols.externalSymbolTable, doRemove = true, keepOptionalAnnotations)
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.acceptVoid(visitor)
|
||||
|
||||
@@ -74,8 +74,8 @@ private val arrayConstructorPhase = makeIrFilePhase(
|
||||
description = "Transform `Array(size) { index -> value }` into a loop"
|
||||
)
|
||||
|
||||
private val expectDeclarationsRemovingPhase = makeIrModulePhase(
|
||||
::ExpectDeclarationsRemoveLowering,
|
||||
private val expectDeclarationsRemovingPhase = makeIrModulePhase<JvmBackendContext>(
|
||||
{ context -> ExpectDeclarationsRemoveLowering(context, keepOptionalAnnotations = true) },
|
||||
name = "ExpectDeclarationsRemoving",
|
||||
description = "Remove expect declaration from module fragment"
|
||||
)
|
||||
|
||||
+8
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.ir.types.isNullable
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
|
||||
import org.jetbrains.kotlin.synthetic.isVisibleOutside
|
||||
import org.jetbrains.org.objectweb.asm.AnnotationVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
@@ -156,6 +157,13 @@ class AnnotationCodegen(
|
||||
val retentionPolicy = getRetentionPolicy(annotationClass)
|
||||
if (retentionPolicy == RetentionPolicy.SOURCE) return null
|
||||
|
||||
// We do not generate annotations whose classes are optional (annotated with `@OptionalExpectation`) because if an annotation entry
|
||||
// is resolved to the expected declaration, this means that annotation has no actual class, and thus should not be generated.
|
||||
// (Otherwise we would've resolved the entry to the actual annotation class.)
|
||||
if (ExpectedActualDeclarationChecker.isOptionalAnnotationClass(annotationClass.descriptor)) {
|
||||
return null
|
||||
}
|
||||
|
||||
innerClassConsumer.addInnerClassInfoFromAnnotation(annotationClass)
|
||||
|
||||
val asmTypeDescriptor = typeMapper.mapType(annotation.type).descriptor
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.util
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.MemberDescriptor
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
@@ -22,7 +23,11 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver
|
||||
|
||||
// `doRemove` means should expect-declaration be removed from IR
|
||||
class ExpectDeclarationRemover(val symbolTable: ReferenceSymbolTable, private val doRemove: Boolean) : IrElementVisitorVoid {
|
||||
class ExpectDeclarationRemover(
|
||||
val symbolTable: ReferenceSymbolTable,
|
||||
private val doRemove: Boolean,
|
||||
private val keepOptionalAnnotations: Boolean
|
||||
) : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
@@ -30,12 +35,16 @@ class ExpectDeclarationRemover(val symbolTable: ReferenceSymbolTable, private va
|
||||
override fun visitFile(declaration: IrFile) {
|
||||
// All declarations with `isExpect == true` are nested into a top-level declaration with `isExpect == true`.
|
||||
declaration.declarations.removeAll {
|
||||
// TODO: rewrite findCompatibleActualForExpected using IR structures instead of descriptors
|
||||
val descriptor = it.descriptor
|
||||
if (descriptor is MemberDescriptor && descriptor.isExpect) {
|
||||
if (descriptor !is MemberDescriptor || !descriptor.isExpect ||
|
||||
(keepOptionalAnnotations && descriptor is ClassDescriptor &&
|
||||
ExpectedActualDeclarationChecker.shouldGenerateExpectClass(descriptor))
|
||||
) {
|
||||
false
|
||||
} else {
|
||||
copyDefaultArgumentsFromExpectToActual(it)
|
||||
doRemove
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,9 +65,7 @@ class ExpectDeclarationRemover(val symbolTable: ReferenceSymbolTable, private va
|
||||
assert(function.valueParameters[index] == declaration)
|
||||
|
||||
if (function is IrConstructor &&
|
||||
ExpectedActualDeclarationChecker.isOptionalAnnotationClass(
|
||||
function.descriptor.constructedClass
|
||||
)
|
||||
ExpectedActualDeclarationChecker.isOptionalAnnotationClass(function.descriptor.constructedClass)
|
||||
) {
|
||||
return
|
||||
}
|
||||
@@ -145,4 +152,4 @@ class ExpectDeclarationRemover(val symbolTable: ReferenceSymbolTable, private va
|
||||
else -> error(parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ fun generateKLib(
|
||||
val moduleName = configuration[CommonConfigurationKeys.MODULE_NAME]!!
|
||||
|
||||
if (!configuration.klibMpp) {
|
||||
moduleFragment.acceptVoid(ExpectDeclarationRemover(psi2IrContext.symbolTable, false))
|
||||
moduleFragment.acceptVoid(ExpectDeclarationRemover(psi2IrContext.symbolTable, doRemove = false, keepOptionalAnnotations = false))
|
||||
}
|
||||
|
||||
serializeModuleIntoKlib(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// !LANGUAGE: +MultiPlatformProjects
|
||||
// !USE_EXPERIMENTAL: kotlin.ExperimentalMultiplatform
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
// FULL_JDK
|
||||
// FILE: A.kt
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// !LANGUAGE: +MultiPlatformProjects
|
||||
// !USE_EXPERIMENTAL: kotlin.ExperimentalMultiplatform
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:Suppress("OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE") // TODO: support common sources in the test infrastructure
|
||||
|
||||
@OptionalExpectation
|
||||
expect annotation class Anno(val s: String)
|
||||
|
||||
// TESTED_OBJECT_KIND: class
|
||||
// TESTED_OBJECTS: Anno
|
||||
// FLAGS: ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
|
||||
+18
@@ -872,6 +872,24 @@ public class WriteFlagsTestGenerated extends AbstractWriteFlagsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/writeFlags/multiplatform")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Multiplatform extends AbstractWriteFlagsTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInMultiplatform() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/writeFlags/multiplatform"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
|
||||
}
|
||||
|
||||
@TestMetadata("optionalExpectation.kt")
|
||||
public void testOptionalExpectation() throws Exception {
|
||||
runTest("compiler/testData/writeFlags/multiplatform/optionalExpectation.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/writeFlags/property")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+18
@@ -872,6 +872,24 @@ public class IrWriteFlagsTestGenerated extends AbstractIrWriteFlagsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/writeFlags/multiplatform")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Multiplatform extends AbstractIrWriteFlagsTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInMultiplatform() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/writeFlags/multiplatform"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@TestMetadata("optionalExpectation.kt")
|
||||
public void testOptionalExpectation() throws Exception {
|
||||
runTest("compiler/testData/writeFlags/multiplatform/optionalExpectation.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/writeFlags/property")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user