From d80a67652f8c152928c5774f482781f007850e50 Mon Sep 17 00:00:00 2001 From: Sergej Jaskiewicz Date: Thu, 8 Feb 2024 19:01:50 +0100 Subject: [PATCH] [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 --- .../ir/descriptors/IrBasedDescriptors.kt | 158 ++++++++++-------- .../signatureClash.diag.txt | 46 ++++- .../klibSerializationTests/signatureClash.kt | 9 + .../SignatureClashDiagnostics/main.kt | 9 + .../SignatureClashDiagnostics/output.txt | 46 +++++ 5 files changed, 196 insertions(+), 72 deletions(-) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBasedDescriptors.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBasedDescriptors.kt index aca8868a00a..1044169a0da 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBasedDescriptors.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBasedDescriptors.kt @@ -44,66 +44,7 @@ import org.jetbrains.kotlin.utils.memoryOptimizedMapIndexed */ abstract class IrBasedDeclarationDescriptor(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 getUserData(key: CallableDescriptor.UserDataKey?): 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)) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/klibSerializationTests/signatureClash.diag.txt b/compiler/testData/diagnostics/klibSerializationTests/signatureClash.diag.txt index e99ba770625..5cc28ad37f4 100644 --- a/compiler/testData/diagnostics/klibSerializationTests/signatureClash.diag.txt +++ b/compiler/testData/diagnostics/klibSerializationTests/signatureClash.diag.txt @@ -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[|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.|(){}[0]): + fun ``(): kotlin.Int defined in com.example.klib.serialization.diagnostics + fun ``(): kotlin.Long defined in com.example.klib.serialization.diagnostics + fun ``(): 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[|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.|(){}[0]): + fun ``(): kotlin.Int defined in com.example.klib.serialization.diagnostics + fun ``(): kotlin.Long defined in com.example.klib.serialization.diagnostics + fun ``(): 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.|(){}[0]): + fun ``(): kotlin.Int defined in com.example.klib.serialization.diagnostics + fun ``(): kotlin.Long defined in com.example.klib.serialization.diagnostics + fun ``(): 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 diff --git a/compiler/testData/diagnostics/klibSerializationTests/signatureClash.kt b/compiler/testData/diagnostics/klibSerializationTests/signatureClash.kt index 88d9e0041a2..afa1011009c 100644 --- a/compiler/testData/diagnostics/klibSerializationTests/signatureClash.kt +++ b/compiler/testData/diagnostics/klibSerializationTests/signatureClash.kt @@ -13,6 +13,8 @@ package com.example.klib.serialization.diagnostics @Deprecated("", level = DeprecationLevel.HIDDEN) fun foo(): Long = 0L +var myVal: Long = 0L + // FILE: main.kt package com.example.klib.serialization.diagnostics @@ -21,6 +23,13 @@ fun foo(): String = "" fun foo(): Int = 0 +@Deprecated("", level = DeprecationLevel.HIDDEN) +var myVal: Int = 0 + +@Deprecated("", level = DeprecationLevel.HIDDEN) +val myVal: String + get() = "" + @Deprecated("This function moved to the 'lib' module", level = DeprecationLevel.HIDDEN) fun movedToLib() {} diff --git a/native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt b/native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt index 66535ff82be..4584cd85c5b 100644 --- a/native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt +++ b/native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt @@ -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() diff --git a/native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/output.txt b/native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/output.txt index 00d81c2c03a..d62adbf2840 100644 --- a/native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/output.txt +++ b/native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/output.txt @@ -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[|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.|(){}[0]): + fun ``(): kotlin.Int defined in com.example.klib.serialization.diagnostics + fun ``(): kotlin.Long defined in com.example.klib.serialization.diagnostics + fun ``(): 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[|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.|(){}[0]): + fun ``(): kotlin.Int defined in com.example.klib.serialization.diagnostics + fun ``(): kotlin.Long defined in com.example.klib.serialization.diagnostics + fun ``(): 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.|(){}[0]): + fun ``(): kotlin.Int defined in com.example.klib.serialization.diagnostics + fun ``(): kotlin.Long defined in com.example.klib.serialization.diagnostics + fun ``(): kotlin.String defined in com.example.klib.serialization.diagnostics + get() = "" + ^^^^^^^^^^ COMPILATION_ERROR