K2: implement annotating record components for @all: use-site target
#KT-73256 Fixed
diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirBlackBoxModernJdkCodegenBasedTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirBlackBoxModernJdkCodegenBasedTestGenerated.java
index 0b80f2d..ea75f41 100644
--- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirBlackBoxModernJdkCodegenBasedTestGenerated.java
+++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirBlackBoxModernJdkCodegenBasedTestGenerated.java
@@ -341,6 +341,12 @@
@TestDataPath("$PROJECT_ROOT")
public class Records {
@Test
+ @TestMetadata("allAnnotation.kt")
+ public void testAllAnnotation() {
+ runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/records/allAnnotation.kt");
+ }
+
+ @Test
public void testAllFilesPresentInRecords() {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxModernJdk/testsWithJava17/records"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirReversedBlackBoxModernJdkCodegenBasedTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirReversedBlackBoxModernJdkCodegenBasedTestGenerated.java
index c978457..d1d9fe7 100644
--- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirReversedBlackBoxModernJdkCodegenBasedTestGenerated.java
+++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirReversedBlackBoxModernJdkCodegenBasedTestGenerated.java
@@ -341,6 +341,12 @@
@TestDataPath("$PROJECT_ROOT")
public class Records {
@Test
+ @TestMetadata("allAnnotation.kt")
+ public void testAllAnnotation() {
+ runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/records/allAnnotation.kt");
+ }
+
+ @Test
public void testAllFilesPresentInRecords() {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxModernJdk/testsWithJava17/records"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBuilderRecord.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBuilderRecord.kt
index eaf3adc..49b53ac 100644
--- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBuilderRecord.kt
+++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBuilderRecord.kt
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.codegen
-fun ClassBuilder.addRecordComponent(name: String, desc: String, signature: String?) {
- newRecordComponent(name, desc, signature)
+import org.jetbrains.org.objectweb.asm.RecordComponentVisitor
+
+fun ClassBuilder.addRecordComponent(name: String, desc: String, signature: String?): RecordComponentVisitor {
+ return newRecordComponent(name, desc, signature)
}
diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt
index 19159f6..0718106 100644
--- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt
+++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt
@@ -9,6 +9,7 @@
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ClassKind
+import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.backend.utils.*
@@ -42,6 +43,7 @@
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
+import org.jetbrains.kotlin.ir.isAnnotationWithAllUseSiteTarget
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.dump
@@ -957,7 +959,11 @@
typeArgumentsCount = fullyExpandedConstructorSymbol.typeParameterSymbols.size,
constructorTypeArgumentsCount = 0,
source = FirAnnotationSourceElement(annotation),
- )
+ ).apply {
+ if (annotation.useSiteTarget == AnnotationUseSiteTarget.ALL) {
+ this.isAnnotationWithAllUseSiteTarget = true
+ }
+ }
}
}
}
diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxModernJdkCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxModernJdkCodegenTestGenerated.java
index e383b5c..21cefbe 100644
--- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxModernJdkCodegenTestGenerated.java
+++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxModernJdkCodegenTestGenerated.java
@@ -342,6 +342,12 @@
@TestDataPath("$PROJECT_ROOT")
public class Records {
@Test
+ @TestMetadata("allAnnotation.kt")
+ public void testAllAnnotation() {
+ runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/records/allAnnotation.kt");
+ }
+
+ @Test
public void testAllFilesPresentInRecords() {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxModernJdk/testsWithJava17/records"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxModernJdkCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxModernJdkCodegenTestGenerated.java
index c46eabf..5a51e0e 100644
--- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxModernJdkCodegenTestGenerated.java
+++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxModernJdkCodegenTestGenerated.java
@@ -342,6 +342,12 @@
@TestDataPath("$PROJECT_ROOT")
public class Records {
@Test
+ @TestMetadata("allAnnotation.kt")
+ public void testAllAnnotation() {
+ runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/records/allAnnotation.kt");
+ }
+
+ @Test
public void testAllFilesPresentInRecords() {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxModernJdk/testsWithJava17/records"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/AnnotationCodegen.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/AnnotationCodegen.kt
index ba675d3..2475742 100644
--- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/AnnotationCodegen.kt
+++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/AnnotationCodegen.kt
@@ -32,6 +32,7 @@
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
+import org.jetbrains.kotlin.ir.isAnnotationWithAllUseSiteTarget
import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
@@ -78,6 +79,10 @@
}
continue
}
+ if (annotated is IrProperty && this is RecordComponentAnnotationCodegen) {
+ if (KotlinTarget.PROPERTY !in applicableTargets) continue
+ if (annotation.isAnnotationWithAllUseSiteTarget != true) continue
+ }
genAnnotation(annotation, null, false)?.let { descriptor ->
annotationDescriptorsAlreadyPresent.add(descriptor)
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 a68c23d..548c2b9 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
@@ -388,8 +388,12 @@
}
if (irClass.hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME) && !field.isStatic) {
- // TODO: Write annotations to the component
- visitor.addRecordComponent(fieldName, fieldType.descriptor, fieldSignature)
+ val rcv = visitor.addRecordComponent(fieldName, fieldType.descriptor, fieldSignature)
+ if (context.config.languageVersionSettings.supportsFeature(LanguageFeature.AnnotationAllUseSiteTarget)) {
+ val recordComponentAnnotationCodegen = RecordComponentAnnotationCodegen(this@ClassCodegen, rcv)
+ // Probably we should filter out non-all and inapplicable annotations
+ field.correspondingPropertySymbol?.owner?.let { recordComponentAnnotationCodegen.genAnnotations(it) }
+ }
}
}
@@ -636,3 +640,12 @@
// From `isAnonymousClass` in inlineCodegenUtils.kt
private val Type.isAnonymousClass: Boolean
get() = internalName.substringAfterLast("$", "").toIntOrNull() != null
+
+class RecordComponentAnnotationCodegen(
+ classCodegen: ClassCodegen,
+ private val rcv: RecordComponentVisitor,
+) : AnnotationCodegen(classCodegen) {
+ override fun visitAnnotation(descr: String, visible: Boolean): AnnotationVisitor {
+ return rcv.visitAnnotation(descr, visible)
+ }
+}
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AdditionalClassAnnotationLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AdditionalClassAnnotationLowering.kt
index 2d5fa28..76c357e 100644
--- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AdditionalClassAnnotationLowering.kt
+++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AdditionalClassAnnotationLowering.kt
@@ -9,6 +9,9 @@
import org.jetbrains.kotlin.backend.common.phaser.PhaseDescription
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.builtins.StandardNames
+import org.jetbrains.kotlin.config.JvmTarget
+import org.jetbrains.kotlin.config.LanguageFeature
+import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.IrClass
@@ -84,7 +87,7 @@
if (irClass.hasAnnotation(JvmAnnotationNames.TARGET_ANNOTATION)) return
val targets = irClass.applicableTargetSet() ?: return
- val javaTargets = targets.mapNotNullTo(HashSet(), ::mapTarget).sortedBy {
+ val javaTargets = targets.mapNotNullTo(HashSet()) { target -> mapTarget(target, irClass) }.sortedBy {
ElementType.valueOf(it.symbol.owner.name.asString())
}
@@ -109,10 +112,16 @@
}
}
- private fun mapTarget(target: KotlinTarget): IrEnumEntry? =
+ private fun mapTarget(target: KotlinTarget, irAnnotationClass: IrClass): IrEnumEntry? =
when (target) {
KotlinTarget.TYPE_PARAMETER -> symbols.typeParameterTarget.takeUnless { noNewJavaAnnotationTargets }
KotlinTarget.TYPE -> symbols.typeUseTarget.takeUnless { noNewJavaAnnotationTargets }
+ KotlinTarget.PROPERTY -> symbols.recordComponentTarget.takeIf {
+ context.config.languageVersionSettings.supportsFeature(LanguageFeature.AnnotationAllUseSiteTarget) &&
+ context.config.target >= JvmTarget.JVM_16 &&
+ irAnnotationClass.getAnnotationRetention().let { it == null || it == KotlinRetention.RUNTIME }
+
+ }
else -> symbols.jvmTargetMap[target]
}
diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt
index 7cdfc2d..03dc7d9 100644
--- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt
+++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt
@@ -1179,6 +1179,7 @@
val typeParameterTarget = buildEnumEntry(elementTypeEnum, "TYPE_PARAMETER")
val typeUseTarget = buildEnumEntry(elementTypeEnum, "TYPE_USE")
+ val recordComponentTarget = buildEnumEntry(elementTypeEnum, "RECORD_COMPONENT")
}
companion object {
diff --git a/compiler/ir/ir.tree/gen/org/jetbrains/kotlin/ir/CommonIrAttributes.kt b/compiler/ir/ir.tree/gen/org/jetbrains/kotlin/ir/CommonIrAttributes.kt
index d8f860a..770be05 100644
--- a/compiler/ir/ir.tree/gen/org/jetbrains/kotlin/ir/CommonIrAttributes.kt
+++ b/compiler/ir/ir.tree/gen/org/jetbrains/kotlin/ir/CommonIrAttributes.kt
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.ir
+import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
+
/**
* Original element before inlining. Useful only with IR
* inliner. `null` if the element wasn't inlined. Unlike [attributeOwnerId], doesn't have the
@@ -12,4 +14,12 @@
*
* `null` <=> `this` element wasn't inlined.
*/
-var IrElement.originalBeforeInline: IrElement? by irAttribute(followAttributeOwner = false)
\ No newline at end of file
+var IrElement.originalBeforeInline: IrElement? by irAttribute(followAttributeOwner = false)
+
+/**
+ * For annotation calls, shows if the original annotation has the all: use-site target.
+ * It's necessary for implementation of RECORD_COMPONENT annotating, that should work only in case when we have this use-site target.
+ *
+ * @return true for an annotation with all: use-site target, null otherwise.
+ */
+var IrConstructorCall.isAnnotationWithAllUseSiteTarget: Boolean? by irAttribute(followAttributeOwner = false)
diff --git a/compiler/testData/codegen/boxModernJdk/testsWithJava17/records/allAnnotation.kt b/compiler/testData/codegen/boxModernJdk/testsWithJava17/records/allAnnotation.kt
new file mode 100644
index 0000000..164c5cf
--- /dev/null
+++ b/compiler/testData/codegen/boxModernJdk/testsWithJava17/records/allAnnotation.kt
@@ -0,0 +1,29 @@
+// IGNORE_BACKEND_K1: JVM_IR
+// ISSUE: KT-73256 (not supported in K1)
+// LANGUAGE: +AnnotationAllUseSiteTarget +PropertyParamAnnotationDefaultTargetMode
+
+annotation class My
+
+@Target(AnnotationTarget.FIELD)
+annotation class Your
+
+@JvmRecord
+data class Some(@all:My val x: Int, @My @field:My val y: Int, @all:Your val z: Int)
+
+fun box(): String {
+ val recordComponents = Some::class.java.recordComponents
+
+ if (recordComponents[0].annotations.isEmpty()) {
+ return "FAIL: no record component annotation for '@all:My val x' found"
+ }
+
+ if (recordComponents[1].annotations.isNotEmpty()) {
+ return "FAIL: record component annotation for '@My val y' found, but it should not be so"
+ }
+
+ if (recordComponents[2].annotations.isNotEmpty()) {
+ return "FAIL: record component annotation for '@all:Your val z' found, but it should not be so"
+ }
+
+ return "OK"
+}
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/JvmAbiConsistencyTestRestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/JvmAbiConsistencyTestRestGenerated.java
index 11932aa..cfcf629 100644
--- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/JvmAbiConsistencyTestRestGenerated.java
+++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/JvmAbiConsistencyTestRestGenerated.java
@@ -6438,6 +6438,12 @@
@TestDataPath("$PROJECT_ROOT")
public class Records {
@Test
+ @TestMetadata("allAnnotation.kt")
+ public void testAllAnnotation() {
+ runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/records/allAnnotation.kt");
+ }
+
+ @Test
public void testAllFilesPresentInRecords() {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxModernJdk/testsWithJava17/records"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxModernJdkCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxModernJdkCodegenTestGenerated.java
index b554d0c..988f70d 100644
--- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxModernJdkCodegenTestGenerated.java
+++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxModernJdkCodegenTestGenerated.java
@@ -342,6 +342,12 @@
@TestDataPath("$PROJECT_ROOT")
public class Records {
@Test
+ @TestMetadata("allAnnotation.kt")
+ public void testAllAnnotation() {
+ runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/records/allAnnotation.kt");
+ }
+
+ @Test
public void testAllFilesPresentInRecords() {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxModernJdk/testsWithJava17/records"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/inlineScopes/FirBlackBoxModernJdkCodegenTestGeneratedWithInlineScopes.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/inlineScopes/FirBlackBoxModernJdkCodegenTestGeneratedWithInlineScopes.java
index 20c7a8f..b2457bc 100644
--- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/inlineScopes/FirBlackBoxModernJdkCodegenTestGeneratedWithInlineScopes.java
+++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/inlineScopes/FirBlackBoxModernJdkCodegenTestGeneratedWithInlineScopes.java
@@ -342,6 +342,12 @@
@TestDataPath("$PROJECT_ROOT")
public class Records {
@Test
+ @TestMetadata("allAnnotation.kt")
+ public void testAllAnnotation() {
+ runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/records/allAnnotation.kt");
+ }
+
+ @Test
public void testAllFilesPresentInRecords() {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxModernJdk/testsWithJava17/records"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}