diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AnnotationGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AnnotationGenerator.kt index 206b0c3680a..bf21601ff4d 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AnnotationGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AnnotationGenerator.kt @@ -7,14 +7,14 @@ package org.jetbrains.kotlin.fir.backend.generators import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.fir.FirAnnotationContainer +import org.jetbrains.kotlin.fir.FirFakeSourceElementKind import org.jetbrains.kotlin.fir.backend.Fir2IrComponents import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.declarations.FirValueParameter -import org.jetbrains.kotlin.fir.declarations.hasMetaAnnotationUseSiteTargets +import org.jetbrains.kotlin.fir.declarations.useSiteTargetsFromMetaAnnotation import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrConstructorCall -import org.jetbrains.kotlin.ir.util.isPropertyField import org.jetbrains.kotlin.ir.util.isSetter /** @@ -36,80 +36,73 @@ class AnnotationGenerator(private val components: Fir2IrComponents) : Fir2IrComp irContainer.annotations = firContainer.annotations.toIrAnnotations() } - fun generate(irValueParameter: IrValueParameter, firValueParameter: FirValueParameter, isInConstructor: Boolean) { - irValueParameter.annotations += - firValueParameter.annotations - .filter { - // TODO: for `null` use-site, refer to targets on the meta annotation - it.useSiteTarget == null || !isInConstructor || it.useSiteTarget == AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER - } - .toIrAnnotations() + private fun FirAnnotationCall.target(applicable: List): AnnotationUseSiteTarget? = + useSiteTarget ?: applicable.firstOrNull(useSiteTargetsFromMetaAnnotation(session)::contains) + + companion object { + // Priority order: constructor parameter (if applicable) -> property -> field. So, for example, if `A` + // can be attached to all three, then in a declaration like + // class C(@A val x: Int) { @A val y = 1 } + // the parameter `x` and the property `y` will have the annotation, while the property `x` and both backing fields will not. + private val propertyTargets = listOf(AnnotationUseSiteTarget.PROPERTY, AnnotationUseSiteTarget.FIELD) + private val constructorPropertyTargets = listOf(AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER) + propertyTargets + private val delegatedPropertyTargets = propertyTargets + listOf(AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD) } - private fun FirAnnotationCall.targetsField(): Boolean = - // Check if the annotation has a field-targeting meta annotation, e.g., @Target(FIELD) - hasMetaAnnotationUseSiteTargets(session, AnnotationUseSiteTarget.FIELD, AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD) + // TODO: third argument should be whether this parameter is a property declaration (though this probably makes no difference) + fun generate(irValueParameter: IrValueParameter, firValueParameter: FirValueParameter, isInConstructor: Boolean) { + if (isInConstructor) { + irValueParameter.annotations += firValueParameter.annotations + .filter { it.target(constructorPropertyTargets) == AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER } + .toIrAnnotations() + } else { + irValueParameter.annotations += firValueParameter.annotations.toIrAnnotations() + } + } fun generate(irProperty: IrProperty, property: FirProperty) { - irProperty.annotations += - property.annotations - .filter { - it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY || - // NB: annotation with null use-site should be landed on the property (ahead of field), - // unless it has FIELD target on the meta annotation, like @Target(FIELD) - (it.useSiteTarget == null && !it.targetsField()) - } - .toIrAnnotations() + val applicableTargets = when { + property.source?.kind == FirFakeSourceElementKind.PropertyFromParameter -> constructorPropertyTargets + irProperty.isDelegated -> delegatedPropertyTargets + else -> propertyTargets + } + irProperty.annotations += property.annotations + .filter { it.target(applicableTargets) == AnnotationUseSiteTarget.PROPERTY } + .toIrAnnotations() } fun generate(irField: IrField, property: FirProperty) { - assert(irField.isPropertyField) { - "$irField is not a property field." + val irProperty = irField.correspondingPropertySymbol?.owner ?: throw AssertionError("$irField is not a property field") + val applicableTargets = when { + property.source?.kind == FirFakeSourceElementKind.PropertyFromParameter -> constructorPropertyTargets + irProperty.isDelegated -> delegatedPropertyTargets + else -> propertyTargets } - irField.annotations += - property.annotations - .filter { - it.useSiteTarget == AnnotationUseSiteTarget.FIELD || - it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD || - (it.useSiteTarget == null && it.targetsField()) - } - .toIrAnnotations() + irField.annotations += property.annotations.filter { + val target = it.target(applicableTargets) + target == AnnotationUseSiteTarget.FIELD || target == AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD + }.toIrAnnotations() } fun generate(propertyAccessor: IrFunction, property: FirProperty) { - assert(propertyAccessor.isPropertyAccessor) { - "$propertyAccessor is not a property accessor." - } + assert(propertyAccessor.isPropertyAccessor) { "$propertyAccessor is not a property accessor." } if (propertyAccessor.isSetter) { - propertyAccessor.annotations += - property.annotations - .filter { - it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_SETTER - } - .toIrAnnotations() - propertyAccessor.valueParameters.singleOrNull()?.annotations = - propertyAccessor.valueParameters.singleOrNull()?.annotations?.plus( - property.annotations - .filter { - it.useSiteTarget == AnnotationUseSiteTarget.SETTER_PARAMETER - } - .toIrAnnotations() - )!! + propertyAccessor.annotations += property.annotations + .filter { it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_SETTER } + .toIrAnnotations() + val parameter = propertyAccessor.valueParameters.single() + parameter.annotations += property.annotations + .filter { it.useSiteTarget == AnnotationUseSiteTarget.SETTER_PARAMETER } + .toIrAnnotations() } else { - propertyAccessor.annotations += - property.annotations - .filter { - it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_GETTER - } - .toIrAnnotations() + propertyAccessor.annotations += property.annotations + .filter { it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_GETTER } + .toIrAnnotations() + } + propertyAccessor.extensionReceiverParameter?.let { receiver -> + receiver.annotations += property.annotations + .filter { it.useSiteTarget == AnnotationUseSiteTarget.RECEIVER } + .toIrAnnotations() } - propertyAccessor.extensionReceiverParameter?.annotations = - propertyAccessor.extensionReceiverParameter?.annotations?.plus( - property.annotations - .filter { - it.useSiteTarget == AnnotationUseSiteTarget.RECEIVER - } - .toIrAnnotations() - )!! } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt index 4a9303f3d65..2701eedf413 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt @@ -54,36 +54,36 @@ inline val FirProperty.hasJvmFieldAnnotation: Boolean val FirAnnotationCall.isJvmFieldAnnotation: Boolean get() = toAnnotationClassId() == JVM_FIELD_CLASS_ID -private fun FirAnnotationCall.useSiteTargetsFromMetaAnnotation(session: FirSession): List { - val metaAnnotationAboutTarget = - toAnnotationClass(session)?.annotations?.find { it.toAnnotationClassId() == TARGET_CLASS_ID } - ?: return emptyList() - return metaAnnotationAboutTarget.argumentList.arguments.toAnnotationUseSiteTargets() -} +fun FirAnnotationCall.useSiteTargetsFromMetaAnnotation(session: FirSession): Set = + toAnnotationClass(session)?.annotations?.find { it.toAnnotationClassId() == TARGET_CLASS_ID }?.argumentList?.arguments + ?.toAnnotationUseSiteTargets() ?: DEFAULT_USE_SITE_TARGETS -private fun List.toAnnotationUseSiteTargets(): List = - flatMap { arg -> - val unwrappedArg = if (arg is FirNamedArgumentExpression) arg.expression else arg - if (unwrappedArg is FirArrayOfCall) { - unwrappedArg.argumentList.arguments.toAnnotationUseSiteTargets() - } else { - unwrappedArg.toAnnotationUseSiteTarget()?.let { listOf(it) } ?: emptyList() +private fun List.toAnnotationUseSiteTargets(): Set = + flatMapTo(mutableSetOf()) { arg -> + when (val unwrappedArg = if (arg is FirNamedArgumentExpression) arg.expression else arg) { + is FirArrayOfCall -> unwrappedArg.argumentList.arguments.toAnnotationUseSiteTargets() + is FirVarargArgumentsExpression -> unwrappedArg.arguments.toAnnotationUseSiteTargets() + else -> USE_SITE_TARGET_NAME_MAP[unwrappedArg.callableNameOfMetaAnnotationArgument?.identifier] ?: setOf() } } -private val USE_SITE_TARGET_NAME_MAP = - AnnotationUseSiteTarget.values().map { it.name to it }.toMap() +// See [org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.USE_SITE_MAPPING] (it's in reverse) +private val USE_SITE_TARGET_NAME_MAP = mapOf( + "FIELD" to setOf(AnnotationUseSiteTarget.FIELD, AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD), + "FILE" to setOf(AnnotationUseSiteTarget.FILE), + "PROPERTY" to setOf(AnnotationUseSiteTarget.PROPERTY), + "PROPERTY_GETTER" to setOf(AnnotationUseSiteTarget.PROPERTY_GETTER), + "PROPERTY_SETTER" to setOf(AnnotationUseSiteTarget.PROPERTY_SETTER), + "VALUE_PARAMETER" to setOf( + AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER, + AnnotationUseSiteTarget.RECEIVER, + AnnotationUseSiteTarget.SETTER_PARAMETER, + ), +) -private fun FirExpression.toAnnotationUseSiteTarget(): AnnotationUseSiteTarget? = - // TODO: depending on the context, "PARAMETER" can be mapped to either CONSTRUCTOR_PARAMETER or SETTER_PARAMETER ? - callableNameOfMetaAnnotationArgument?.identifier?.let { - USE_SITE_TARGET_NAME_MAP[it] - } - -fun FirAnnotationCall.hasMetaAnnotationUseSiteTargets(session: FirSession, vararg useSiteTargets: AnnotationUseSiteTarget): Boolean { - val meta = useSiteTargetsFromMetaAnnotation(session) - return useSiteTargets.any { meta.contains(it) } -} +// See [org.jetbrains.kotlin.descriptors.annotations.KotlinTarget] (the second argument of each entry) +private val DEFAULT_USE_SITE_TARGETS: Set = + USE_SITE_TARGET_NAME_MAP.values.fold(setOf()) { a, b -> a + b } - setOf(AnnotationUseSiteTarget.FILE) fun FirAnnotatedDeclaration.hasAnnotation(classId: ClassId): Boolean { return annotations.any { it.toAnnotationClassId() == classId } diff --git a/compiler/testData/codegen/box/annotations/annotationsOnLateinitFields.kt b/compiler/testData/codegen/box/annotations/annotationsOnLateinitFields.kt index 4beb41d12d2..8878e9d07ee 100644 --- a/compiler/testData/codegen/box/annotations/annotationsOnLateinitFields.kt +++ b/compiler/testData/codegen/box/annotations/annotationsOnLateinitFields.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR // WITH_REFLECT // TARGET_BACKEND: JVM