[klib] Fix NIE when showing signature clash diagnostics on properties

The issue was that when rendering declarations in
the `CONFLICTING_KLIB_SIGNATURES_DATA` diagnostics, we sort them using
`MemberComparator`. That comparator falls back to comparing
declarations' renders if all previous checks were unsuccessful
(and in case of almost identical properties they are). The renderer that
the comparator uses also renders the properties' backing field
annotations, for which it calls `PropertyDescriptor#getBackingField`.
That method wasn't implemented in IR-based descriptors.

This is fixed by returning an instance of the new
`IrBasedBackingFieldDescriptor` class from that method.

^KT-65551 Fixed
This commit is contained in:
Sergej Jaskiewicz
2024-02-08 19:01:50 +01:00
committed by Space Team
parent 9688c3e761
commit d80a67652f
5 changed files with 196 additions and 72 deletions
@@ -44,66 +44,7 @@ import org.jetbrains.kotlin.utils.memoryOptimizedMapIndexed
*/
abstract class IrBasedDeclarationDescriptor<T : IrDeclaration>(val owner: T) : DeclarationDescriptor {
override val annotations: Annotations by lazy {
val ownerAnnotations = (owner as? IrAnnotationContainer)?.annotations ?: return@lazy Annotations.EMPTY
Annotations.create(ownerAnnotations.memoryOptimizedMap { it.toAnnotationDescriptor() })
}
private fun IrConstructorCall.toAnnotationDescriptor(): AnnotationDescriptor {
val annotationClass = symbol.owner.parentAsClass
@OptIn(ObsoleteDescriptorBasedAPI::class)
if (annotationClass.symbol.descriptor == ErrorUtils.errorClass) {
// This should be possible only in case of KAPT3 where IR generated by psi2ir can have annotations with unresolved types.
// Apparently annotations with unresolved types is a useful feature for KAPT3 in the "correct error types mode", see kt32596.kt.
// This is a hack which should preferably be removed as soon as KAPT3 is no longer used.
return AnnotationDescriptorImpl(type.toKotlinType(), emptyMap(), source)
}
assert(annotationClass.isAnnotationClass) {
"Expected call to constructor of annotation class but was: ${this.dump()}"
}
return AnnotationDescriptorImpl(
annotationClass.defaultType.toIrBasedKotlinType(),
symbol.owner.valueParameters.memoryOptimizedMap { it.name to getValueArgument(it.index) }
.filter { it.second != null }
.associate { it.first to it.second!!.toConstantValue() },
source
)
}
private fun IrElement.toConstantValue(): ConstantValue<*> {
return when (this) {
is IrConst<*> -> when (kind) {
IrConstKind.Null -> NullValue()
IrConstKind.Boolean -> BooleanValue(value as Boolean)
IrConstKind.Char -> CharValue(value as Char)
IrConstKind.Byte -> ByteValue(value as Byte)
IrConstKind.Short -> ShortValue(value as Short)
IrConstKind.Int -> IntValue(value as Int)
IrConstKind.Long -> LongValue(value as Long)
IrConstKind.String -> StringValue(value as String)
IrConstKind.Float -> FloatValue(value as Float)
IrConstKind.Double -> DoubleValue(value as Double)
}
is IrVararg -> {
val elements = elements.memoryOptimizedMap { if (it is IrSpreadElement) error("$it is not expected") else it.toConstantValue() }
ArrayValue(elements) { moduleDescriptor ->
// TODO: substitute.
moduleDescriptor.builtIns.array.defaultType
}
}
is IrGetEnumValue -> EnumValue(symbol.owner.parentAsClass.toIrBasedDescriptor().classId!!, symbol.owner.name)
is IrClassReference -> KClassValue((classType.classifierOrFail.owner as IrClass).toIrBasedDescriptor().classId!!, /*TODO*/0)
is IrConstructorCall -> AnnotationValue(this.toAnnotationDescriptor())
else -> error("$this is not expected: ${this.dump()}")
}
}
override val annotations: Annotations by lazy(owner::toAnnotations)
@OptIn(ObsoleteDescriptorBasedAPI::class)
protected fun IrType.toIrBasedKotlinType(): KotlinType = toIrBasedKotlinType(owner.module.builtIns)
@@ -922,19 +863,27 @@ open class IrBasedPropertyDescriptor(owner: IrProperty) :
override val isDelegated get() = owner.isDelegated
override fun getBackingField(): FieldDescriptor? {
TODO("not implemented")
}
override fun getBackingField(): FieldDescriptor? =
owner.backingField?.toIrBackingFieldDescriptor(isDelegate = false, correspondingProperty = this)
override fun getDelegateField(): FieldDescriptor? {
TODO("not implemented")
}
override fun getDelegateField(): FieldDescriptor? =
owner.backingField?.toIrBackingFieldDescriptor(isDelegate = true, correspondingProperty = this)
override fun getInType(): KotlinType? = setter?.valueParameters?.get(0)?.type
override fun <V : Any?> getUserData(key: CallableDescriptor.UserDataKey<V>?): V? = null
}
private fun IrField.toIrBackingFieldDescriptor(
isDelegate: Boolean,
correspondingProperty: PropertyDescriptor,
): IrBasedBackingFieldDescriptor? =
if (isDelegate == (origin == IrDeclarationOrigin.PROPERTY_DELEGATE)) {
IrBasedBackingFieldDescriptor(this, correspondingProperty)
} else {
null
}
fun IrProperty.toIrBasedDescriptor() = IrBasedPropertyDescriptor(this)
abstract class IrBasedPropertyAccessorDescriptor(owner: IrSimpleFunction) : IrBasedSimpleFunctionDescriptor(owner),
@@ -959,6 +908,15 @@ open class IrBasedPropertyGetterDescriptor(owner: IrSimpleFunction) : IrBasedPro
visitor!!.visitPropertyGetterDescriptor(this, data)
}
class IrBasedBackingFieldDescriptor(val owner: IrField, override val correspondingProperty: PropertyDescriptor) : FieldDescriptor {
override val annotations: Annotations by lazy(owner::toAnnotations)
override fun equals(other: Any?): Boolean = other is IrBasedBackingFieldDescriptor && owner == other.owner
override fun hashCode(): Int = owner.hashCode()
}
open class IrBasedPropertySetterDescriptor(owner: IrSimpleFunction) : IrBasedPropertyAccessorDescriptor(owner), PropertySetterDescriptor {
override fun getOverriddenDescriptors() = super.getOverriddenDescriptors().memoryOptimizedMap { it as PropertySetterDescriptor }
@@ -1119,8 +1077,11 @@ open class IrBasedFieldDescriptor(owner: IrField) : PropertyDescriptor, IrBasedD
override val isDelegated get() = false
// Following functions are used in error reporting when rendering annotations on properties
override fun getBackingField(): FieldDescriptor? = null // TODO
override fun getDelegateField(): FieldDescriptor? = null // TODO
override fun getBackingField(): FieldDescriptor? =
owner.toIrBackingFieldDescriptor(isDelegate = false, correspondingProperty = this)
override fun getDelegateField(): FieldDescriptor? =
owner.toIrBackingFieldDescriptor(isDelegate = true, correspondingProperty = this)
override fun getInType(): KotlinType? = setter?.valueParameters?.get(0)?.type
@@ -1269,3 +1230,64 @@ private fun IrScriptSymbol.toIrBasedDescriptorIfPossible(): ScriptDescriptor = d
// see comment above to IrScriptSymbol.toIrBasedDescriptorIfPossible()
@OptIn(ObsoleteDescriptorBasedAPI::class)
private fun IrScript.toIrBasedDescriptor() = descriptor
private fun IrElement.toConstantValue(): ConstantValue<*> {
return when (this) {
is IrConst<*> -> when (kind) {
IrConstKind.Null -> NullValue()
IrConstKind.Boolean -> BooleanValue(value as Boolean)
IrConstKind.Char -> CharValue(value as Char)
IrConstKind.Byte -> ByteValue(value as Byte)
IrConstKind.Short -> ShortValue(value as Short)
IrConstKind.Int -> IntValue(value as Int)
IrConstKind.Long -> LongValue(value as Long)
IrConstKind.String -> StringValue(value as String)
IrConstKind.Float -> FloatValue(value as Float)
IrConstKind.Double -> DoubleValue(value as Double)
}
is IrVararg -> {
val elements = elements.memoryOptimizedMap { if (it is IrSpreadElement) error("$it is not expected") else it.toConstantValue() }
ArrayValue(elements) { moduleDescriptor ->
// TODO: substitute.
moduleDescriptor.builtIns.array.defaultType
}
}
is IrGetEnumValue -> EnumValue(symbol.owner.parentAsClass.toIrBasedDescriptor().classId!!, symbol.owner.name)
is IrClassReference -> KClassValue((classType.classifierOrFail.owner as IrClass).toIrBasedDescriptor().classId!!, /*TODO*/0)
is IrConstructorCall -> AnnotationValue(this.toAnnotationDescriptor())
else -> error("$this is not expected: ${this.dump()}")
}
}
private fun IrConstructorCall.toAnnotationDescriptor(): AnnotationDescriptor {
val annotationClass = symbol.owner.parentAsClass
@OptIn(ObsoleteDescriptorBasedAPI::class)
if (annotationClass.symbol.descriptor == ErrorUtils.errorClass) {
// This should be possible only in case of KAPT3 where IR generated by psi2ir can have annotations with unresolved types.
// Apparently annotations with unresolved types is a useful feature for KAPT3 in the "correct error types mode", see kt32596.kt.
// This is a hack which should preferably be removed as soon as KAPT3 is no longer used.
return AnnotationDescriptorImpl(type.toKotlinType(), emptyMap(), source)
}
assert(annotationClass.isAnnotationClass) {
"Expected call to constructor of annotation class but was: ${this.dump()}"
}
return AnnotationDescriptorImpl(
annotationClass.defaultType.toIrBasedKotlinType(),
symbol.owner.valueParameters.memoryOptimizedMap { it.name to getValueArgument(it.index) }
.filter { it.second != null }
.associate { it.first to it.second!!.toConstantValue() },
source
)
}
private fun IrDeclaration.toAnnotations(): Annotations {
val ownerAnnotations = (this as? IrAnnotationContainer)?.annotations ?: return Annotations.EMPTY
return Annotations.create(ownerAnnotations.memoryOptimizedMap(IrConstructorCall::toAnnotationDescriptor))
}
@@ -3,20 +3,58 @@
fun foo(): kotlin.String defined in com.example.klib.serialization.diagnostics
fun foo(): kotlin.Int defined in com.example.klib.serialization.diagnostics
/main.kt:19:1: error: Platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/foo|foo(){}[0]):
/foo.kt:16:1: error: Platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/myVal|{}myVal[0]):
val myVal: kotlin.String defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Int defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Long defined in com.example.klib.serialization.diagnostics
/foo.kt:16:1: error: Platform declaration clash: The following declarations have the same IR signature ([ com.example.klib.serialization.diagnostics/myVal|{}myVal[0] <- Local[<BF>|FIELD PROPERTY_BACKING_FIELD name:myVal type:kotlin.Long visibility:private [static]] ]):
var myVal: kotlin.Int defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Long defined in com.example.klib.serialization.diagnostics
/foo.kt:16:1: error: Platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/myVal.<get-myVal>|<get-myVal>(){}[0]):
fun `<get-myVal>`(): kotlin.Int defined in com.example.klib.serialization.diagnostics
fun `<get-myVal>`(): kotlin.Long defined in com.example.klib.serialization.diagnostics
fun `<get-myVal>`(): kotlin.String defined in com.example.klib.serialization.diagnostics
/main.kt:21:1: error: Platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/foo|foo(){}[0]):
fun foo(): kotlin.Long defined in com.example.klib.serialization.diagnostics
fun foo(): kotlin.String defined in com.example.klib.serialization.diagnostics
fun foo(): kotlin.Int defined in com.example.klib.serialization.diagnostics
/main.kt:22:1: error: Platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/foo|foo(){}[0]):
/main.kt:24:1: error: Platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/foo|foo(){}[0]):
fun foo(): kotlin.Long defined in com.example.klib.serialization.diagnostics
fun foo(): kotlin.String defined in com.example.klib.serialization.diagnostics
fun foo(): kotlin.Int defined in com.example.klib.serialization.diagnostics
/main.kt:35:1: error: Platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/bar|bar(){}[0]):
/main.kt:26:1: error: Platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/myVal|{}myVal[0]):
val myVal: kotlin.String defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Int defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Long defined in com.example.klib.serialization.diagnostics
/main.kt:26:1: error: Platform declaration clash: The following declarations have the same IR signature ([ com.example.klib.serialization.diagnostics/myVal|{}myVal[0] <- Local[<BF>|FIELD PROPERTY_BACKING_FIELD name:myVal type:kotlin.Long visibility:private [static]] ]):
var myVal: kotlin.Int defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Long defined in com.example.klib.serialization.diagnostics
/main.kt:26:1: error: Platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/myVal.<get-myVal>|<get-myVal>(){}[0]):
fun `<get-myVal>`(): kotlin.Int defined in com.example.klib.serialization.diagnostics
fun `<get-myVal>`(): kotlin.Long defined in com.example.klib.serialization.diagnostics
fun `<get-myVal>`(): kotlin.String defined in com.example.klib.serialization.diagnostics
/main.kt:29:1: error: Platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/myVal|{}myVal[0]):
val myVal: kotlin.String defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Int defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Long defined in com.example.klib.serialization.diagnostics
/main.kt:31:5: error: Platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/myVal.<get-myVal>|<get-myVal>(){}[0]):
fun `<get-myVal>`(): kotlin.Int defined in com.example.klib.serialization.diagnostics
fun `<get-myVal>`(): kotlin.Long defined in com.example.klib.serialization.diagnostics
fun `<get-myVal>`(): kotlin.String defined in com.example.klib.serialization.diagnostics
/main.kt:44:1: error: Platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/bar|bar(){}[0]):
fun bar(): kotlin.Long defined in com.example.klib.serialization.diagnostics
fun bar(): kotlin.Long defined in com.example.klib.serialization.diagnostics
/main.kt:38:1: error: Platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/bar|bar(){}[0]):
/main.kt:47:1: error: Platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/bar|bar(){}[0]):
fun bar(): kotlin.Long defined in com.example.klib.serialization.diagnostics
fun bar(): kotlin.Long defined in com.example.klib.serialization.diagnostics
@@ -13,6 +13,8 @@ package com.example.klib.serialization.diagnostics
<!CONFLICTING_KLIB_SIGNATURES_ERROR!>@Deprecated("", level = DeprecationLevel.HIDDEN)
fun foo(): Long = 0L<!>
<!CONFLICTING_KLIB_SIGNATURES_ERROR, CONFLICTING_KLIB_SIGNATURES_ERROR, CONFLICTING_KLIB_SIGNATURES_ERROR!>var myVal: Long = 0L<!>
// FILE: main.kt
package com.example.klib.serialization.diagnostics
@@ -21,6 +23,13 @@ fun foo(): String = ""<!>
<!CONFLICTING_KLIB_SIGNATURES_ERROR!>fun foo(): Int = 0<!>
<!CONFLICTING_KLIB_SIGNATURES_ERROR, CONFLICTING_KLIB_SIGNATURES_ERROR, CONFLICTING_KLIB_SIGNATURES_ERROR!>@Deprecated("", level = DeprecationLevel.HIDDEN)
var myVal: Int = 0<!>
<!CONFLICTING_KLIB_SIGNATURES_ERROR!>@Deprecated("", level = DeprecationLevel.HIDDEN)
val myVal: String
<!CONFLICTING_KLIB_SIGNATURES_ERROR!>get() = ""<!><!>
@Deprecated("This function moved to the 'lib' module", level = DeprecationLevel.HIDDEN)
fun movedToLib() {}
@@ -7,9 +7,18 @@ class A {
fun foo(): Int = 0
}
var myVal: Long = 0L
@Deprecated("This function moved to the 'lib' module", level = DeprecationLevel.HIDDEN)
fun movedToLib() {}
@Deprecated("", level = DeprecationLevel.HIDDEN)
var myVal: Int = 0
@Deprecated("", level = DeprecationLevel.HIDDEN)
val myVal: String
get() = ""
fun main() {
println(A().foo())
movedToLib()
@@ -8,4 +8,50 @@ native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt:7:
fun foo(): kotlin.Int defined in com.example.klib.serialization.diagnostics.A
fun foo(): Int = 0
^^^^^^^^^^^^^^^^^^
native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt:10:1: error: platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/myVal|{}myVal[0]):
val myVal: kotlin.String defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Int defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Long defined in com.example.klib.serialization.diagnostics
var myVal: Long = 0L
^^^^^^^^^^^^^^^^^^^^
native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt:10:1: error: platform declaration clash: The following declarations have the same IR signature ([ com.example.klib.serialization.diagnostics/myVal|{}myVal[0] <- Local[<BF>|FIELD PROPERTY_BACKING_FIELD name:myVal type:kotlin.Long visibility:private [static]] ]):
var myVal: kotlin.Int defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Long defined in com.example.klib.serialization.diagnostics
var myVal: Long = 0L
^^^^^^^^^^^^^^^^^^^^
native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt:10:1: error: platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/myVal.<get-myVal>|<get-myVal>(){}[0]):
fun `<get-myVal>`(): kotlin.Int defined in com.example.klib.serialization.diagnostics
fun `<get-myVal>`(): kotlin.Long defined in com.example.klib.serialization.diagnostics
fun `<get-myVal>`(): kotlin.String defined in com.example.klib.serialization.diagnostics
var myVal: Long = 0L
^^^^^^^^^^^^^^^^^^^^
native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt:15:1: error: platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/myVal|{}myVal[0]):
val myVal: kotlin.String defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Int defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Long defined in com.example.klib.serialization.diagnostics
@Deprecated("", level = DeprecationLevel.HIDDEN)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt:15:1: error: platform declaration clash: The following declarations have the same IR signature ([ com.example.klib.serialization.diagnostics/myVal|{}myVal[0] <- Local[<BF>|FIELD PROPERTY_BACKING_FIELD name:myVal type:kotlin.Long visibility:private [static]] ]):
var myVal: kotlin.Int defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Long defined in com.example.klib.serialization.diagnostics
@Deprecated("", level = DeprecationLevel.HIDDEN)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt:15:1: error: platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/myVal.<get-myVal>|<get-myVal>(){}[0]):
fun `<get-myVal>`(): kotlin.Int defined in com.example.klib.serialization.diagnostics
fun `<get-myVal>`(): kotlin.Long defined in com.example.klib.serialization.diagnostics
fun `<get-myVal>`(): kotlin.String defined in com.example.klib.serialization.diagnostics
@Deprecated("", level = DeprecationLevel.HIDDEN)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt:18:1: error: platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/myVal|{}myVal[0]):
val myVal: kotlin.String defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Int defined in com.example.klib.serialization.diagnostics
var myVal: kotlin.Long defined in com.example.klib.serialization.diagnostics
@Deprecated("", level = DeprecationLevel.HIDDEN)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt:20:5: error: platform declaration clash: The following declarations have the same IR signature (com.example.klib.serialization.diagnostics/myVal.<get-myVal>|<get-myVal>(){}[0]):
fun `<get-myVal>`(): kotlin.Int defined in com.example.klib.serialization.diagnostics
fun `<get-myVal>`(): kotlin.Long defined in com.example.klib.serialization.diagnostics
fun `<get-myVal>`(): kotlin.String defined in com.example.klib.serialization.diagnostics
get() = ""
^^^^^^^^^^
COMPILATION_ERROR