diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt index ac527d8b75d..03947b7aa78 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt @@ -95,7 +95,7 @@ class Fir2IrDeclarationStorage( private val fakeOverridesInClass = mutableMapOf, FirCallableDeclaration<*>>>() // For pure fields (from Java) only - private val fieldToPropertyCache = ConcurrentHashMap() + private val fieldToPropertyCache = ConcurrentHashMap, IrProperty>() private val delegatedReverseCache = ConcurrentHashMap() @@ -719,9 +719,13 @@ class Fir2IrDeclarationStorage( field: FirField, irParent: IrDeclarationParent ): IrProperty { - fieldToPropertyCache[field]?.let { return it } - return createIrProperty(field.toStubProperty(), irParent).apply { - fieldToPropertyCache[field] = this + return fieldToPropertyCache.getOrPut(field to irParent) { + val containingClassId = (irParent as? IrClass)?.classId + createIrProperty( + field.toStubProperty(), + irParent, + containingClass = containingClassId?.let { ConeClassLikeLookupTagImpl(it) } + ) } } 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 37ff1d16a58..8cef8b6556e 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 @@ -39,6 +39,7 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.* +import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.psi2ir.generators.hasNoSideEffects @@ -103,7 +104,8 @@ class CallAndReferenceGenerator( ) } is IrFieldSymbol -> { - val referencedField = symbol.owner + val fieldSymbol = computeFieldSymbolForCallableReference(callableReferenceAccess, symbol) + val referencedField = fieldSymbol.owner val propertySymbol = referencedField.correspondingPropertySymbol ?: run { // In case of [IrField] without the corresponding property, we've created it directly from [FirField]. @@ -116,7 +118,7 @@ class CallAndReferenceGenerator( startOffset, endOffset, type, propertySymbol, typeArgumentsCount = (type as? IrSimpleType)?.arguments?.size ?: 0, - field = symbol, + field = fieldSymbol, getter = if (referencedField.isStatic) null else propertySymbol.owner.getter?.symbol, setter = if (referencedField.isStatic) null else propertySymbol.owner.setter?.symbol, origin @@ -154,6 +156,33 @@ class CallAndReferenceGenerator( }.applyTypeArguments(callableReferenceAccess).applyReceivers(callableReferenceAccess, explicitReceiverExpression) } + private fun computeFieldSymbolForCallableReference( + callableReferenceAccess: FirCallableReferenceAccess, + symbol: IrFieldSymbol + ): IrFieldSymbol { + val receiverClass = when (val receiverExpression = callableReferenceAccess.explicitReceiver) { + is FirResolvedQualifier -> receiverExpression.symbol + null -> null + else -> receiverExpression.typeRef.coneType.toSymbol(session) + } as? FirClassSymbol<*> + val newParent = receiverClass?.let { classifierStorage.getIrClassSymbol(it) }?.owner ?: return symbol + return IrFieldSymbolImpl().also { newSymbol -> + val field = symbol.owner + irFactory.createField( + startOffset = -1, + endOffset = -1, + field.origin, + newSymbol, + field.name, + field.type, + field.visibility, + field.isFinal, + field.isExternal, + field.isStatic, + ).also { it.parent = newParent } + } + } + private fun FirQualifiedAccess.tryConvertToSamConstructorCall(type: IrType): IrTypeOperatorCall? { val calleeReference = calleeReference as? FirResolvedNamedReference ?: return null val fir = calleeReference.resolvedSymbol.fir diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index 78e15ced947..f01cde503d4 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -15098,6 +15098,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/fir"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("callableReferenceToJavaField.kt") + public void testCallableReferenceToJavaField() throws Exception { + runTest("compiler/testData/codegen/box/fir/callableReferenceToJavaField.kt"); + } + @Test @TestMetadata("callableReferenceToStaticFunction.kt") public void testCallableReferenceToStaticFunction() throws Exception { diff --git a/compiler/testData/codegen/box/fir/callableReferenceToJavaField.fir.txt b/compiler/testData/codegen/box/fir/callableReferenceToJavaField.fir.txt new file mode 100644 index 00000000000..1f9e6599318 --- /dev/null +++ b/compiler/testData/codegen/box/fir/callableReferenceToJavaField.fir.txt @@ -0,0 +1,80 @@ +FILE fqName:foo fileName:/main.kt + CLASS CLASS name:A modality:FINAL visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:foo.A + CONSTRUCTOR visibility:public <> (s:kotlin.String) returnType:foo.A [primary] + VALUE_PARAMETER name:s index:0 type:kotlin.String + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' + INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:A modality:FINAL visibility:public superTypes:[kotlin.Any]' + PROPERTY name:s visibility:public modality:FINAL [val] + FIELD PROPERTY_BACKING_FIELD name:s type:kotlin.String visibility:private [final] + EXPRESSION_BODY + GET_VAR 's: kotlin.String declared in foo.A.' type=kotlin.String origin=INITIALIZE_PROPERTY_FROM_PARAMETER + FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> ($this:foo.A) returnType:kotlin.String + correspondingProperty: PROPERTY name:s visibility:public modality:FINAL [val] + $this: VALUE_PARAMETER name: type:foo.A + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun (): kotlin.String declared in foo.A' + GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:s type:kotlin.String visibility:private [final]' type=kotlin.String origin=null + receiver: GET_VAR ': foo.A declared in foo.A.' type=foo.A origin=null + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + CLASS CLASS name:Derived modality:FINAL visibility:public superTypes:[bar.Base] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:foo.Derived + CONSTRUCTOR visibility:public <> () returnType:foo.Derived [primary] + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in bar.Base' + INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:Derived modality:FINAL visibility:public superTypes:[bar.Base]' + FUN name:foo visibility:protected modality:FINAL <> ($this:foo.Derived) returnType:kotlin.Unit + overridden: + protected/*protected and package*/ open fun foo (): kotlin.Unit declared in bar.Base + $this: VALUE_PARAMETER name: type:foo.Derived + BLOCK_BODY + CALL 'public open fun set (p0: @[FlexibleNullability] kotlin.Any?, p1: @[FlexibleNullability] kotlin.Any?): kotlin.Unit [operator] declared in java.lang.reflect.Field' type=kotlin.Unit origin=null + $this: CALL 'public final fun CHECK_NOT_NULL (arg0: T0 of kotlin.internal.ir.CHECK_NOT_NULL?): T0 of kotlin.internal.ir.CHECK_NOT_NULL declared in kotlin.internal.ir' type=java.lang.reflect.Field origin=EXCLEXCL + : java.lang.reflect.Field + arg0: CALL 'public final fun (): java.lang.reflect.Field? declared in kotlin.reflect.jvm.ReflectJvmMapping' type=java.lang.reflect.Field? origin=GET_PROPERTY + $receiver: PROPERTY_REFERENCE 'protected/*protected and package*/ open a: @[FlexibleNullability] foo.A? [var]' field='FIELD IR_EXTERNAL_JAVA_DECLARATION_STUB name:a type:@[FlexibleNullability] foo.A? visibility:protected/*protected and package*/' getter='protected/*protected and package*/ open fun (): @[FlexibleNullability] foo.A? declared in foo.Derived' setter='protected/*protected and package*/ open fun (: @[FlexibleNullability] foo.A?): kotlin.Unit declared in foo.Derived' type=kotlin.reflect.KMutableProperty1 origin=null + <1>: + <2>: + p0: GET_VAR ': foo.Derived declared in foo.Derived.foo' type=foo.Derived origin=null + p1: CONSTRUCTOR_CALL 'public constructor (s: kotlin.String) [primary] declared in foo.A' type=foo.A origin=null + s: CONST String type=kotlin.String value="OK" + FUN name:box visibility:public modality:FINAL <> ($this:foo.Derived) returnType:kotlin.String + $this: VALUE_PARAMETER name: type:foo.Derived + BLOCK_BODY + CALL 'protected final fun foo (): kotlin.Unit declared in foo.Derived' type=kotlin.Unit origin=null + $this: GET_VAR ': foo.Derived declared in foo.Derived.box' type=foo.Derived origin=null + RETURN type=kotlin.Nothing from='public final fun box (): kotlin.String declared in foo.Derived' + CALL 'public final fun (): kotlin.String declared in foo.A' type=kotlin.String origin=GET_PROPERTY + $this: GET_FIELD 'FIELD IR_EXTERNAL_JAVA_DECLARATION_STUB name:a type:@[FlexibleNullability] foo.A? visibility:protected/*protected and package*/' type=@[FlexibleNullability] foo.A? origin=GET_PROPERTY + receiver: GET_VAR ': foo.Derived declared in foo.Derived.box' type=foo.Derived origin=null + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [fake_override,operator] declared in bar.Base + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int [fake_override] declared in bar.Base + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String [fake_override] declared in bar.Base + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN name:box visibility:public modality:FINAL <> () returnType:kotlin.String + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun box (): kotlin.String declared in foo' + CALL 'public final fun box (): kotlin.String declared in foo.Derived' type=kotlin.String origin=null + $this: CONSTRUCTOR_CALL 'public constructor () [primary] declared in foo.Derived' type=foo.Derived origin=null diff --git a/compiler/testData/codegen/box/fir/callableReferenceToJavaField.kt b/compiler/testData/codegen/box/fir/callableReferenceToJavaField.kt new file mode 100644 index 00000000000..f1f04a6478e --- /dev/null +++ b/compiler/testData/codegen/box/fir/callableReferenceToJavaField.kt @@ -0,0 +1,39 @@ +// TARGET_BACKEND: JVM_IR +// DUMP_IR +// FULL_JDK +// WITH_REFLECT + +// FILE: bar/Base.java +package bar; + +import foo.A; + +public abstract class Base { + protected A a = new A("fail"); + + protected void foo() {} +} + +// FILE: main.kt +package foo + +import kotlin.reflect.jvm.javaField +import bar.Base + +class A(val s: String) + +class Derived : Base() { + override fun foo() { + // ir: resolved to fake-override field Derived.a + (Derived::a).javaField!![this] = A("OK") + } + + fun box(): String { + foo() + return a.s + } +} + +fun box(): String { + return Derived().box() +} diff --git a/compiler/testData/codegen/box/fir/callableReferenceToJavaField.txt b/compiler/testData/codegen/box/fir/callableReferenceToJavaField.txt new file mode 100644 index 00000000000..372796c4ab3 --- /dev/null +++ b/compiler/testData/codegen/box/fir/callableReferenceToJavaField.txt @@ -0,0 +1,80 @@ +FILE fqName:foo fileName:/main.kt + CLASS CLASS name:A modality:FINAL visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:foo.A + CONSTRUCTOR visibility:public <> (s:kotlin.String) returnType:foo.A [primary] + VALUE_PARAMETER name:s index:0 type:kotlin.String + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' + INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:A modality:FINAL visibility:public superTypes:[kotlin.Any]' + PROPERTY name:s visibility:public modality:FINAL [val] + FIELD PROPERTY_BACKING_FIELD name:s type:kotlin.String visibility:private [final] + EXPRESSION_BODY + GET_VAR 's: kotlin.String declared in foo.A.' type=kotlin.String origin=INITIALIZE_PROPERTY_FROM_PARAMETER + FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> ($this:foo.A) returnType:kotlin.String + correspondingProperty: PROPERTY name:s visibility:public modality:FINAL [val] + $this: VALUE_PARAMETER name: type:foo.A + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun (): kotlin.String declared in foo.A' + GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:s type:kotlin.String visibility:private [final]' type=kotlin.String origin=null + receiver: GET_VAR ': foo.A declared in foo.A.' type=foo.A origin=null + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + CLASS CLASS name:Derived modality:FINAL visibility:public superTypes:[bar.Base] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:foo.Derived + CONSTRUCTOR visibility:public <> () returnType:foo.Derived [primary] + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in bar.Base' + INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:Derived modality:FINAL visibility:public superTypes:[bar.Base]' + FUN name:foo visibility:protected modality:OPEN <> ($this:foo.Derived) returnType:kotlin.Unit + overridden: + protected/*protected and package*/ open fun foo (): kotlin.Unit declared in bar.Base + $this: VALUE_PARAMETER name: type:foo.Derived + BLOCK_BODY + CALL 'public open fun set (p0: @[FlexibleNullability] kotlin.Any?, p1: @[FlexibleNullability] kotlin.Any?): kotlin.Unit [operator] declared in java.lang.reflect.Field' type=kotlin.Unit origin=EQ + $this: CALL 'public final fun CHECK_NOT_NULL (arg0: T0 of kotlin.internal.ir.CHECK_NOT_NULL?): T0 of kotlin.internal.ir.CHECK_NOT_NULL declared in kotlin.internal.ir' type=java.lang.reflect.Field origin=EXCLEXCL + : java.lang.reflect.Field + arg0: CALL 'public final fun (): java.lang.reflect.Field? declared in kotlin.reflect.jvm.ReflectJvmMapping' type=java.lang.reflect.Field? origin=GET_PROPERTY + $receiver: PROPERTY_REFERENCE 'protected/*protected and package*/ final a [fake_override,var]' field='FIELD IR_EXTERNAL_JAVA_DECLARATION_STUB name:a type:@[FlexibleNullability] foo.A? visibility:protected/*protected and package*/' getter=null setter=null type=kotlin.reflect.KMutableProperty1 origin=null + p0: GET_VAR ': foo.Derived declared in foo.Derived.foo' type=foo.Derived origin=null + p1: CONSTRUCTOR_CALL 'public constructor (s: kotlin.String) [primary] declared in foo.A' type=foo.A origin=null + s: CONST String type=kotlin.String value="OK" + FUN name:box visibility:public modality:FINAL <> ($this:foo.Derived) returnType:kotlin.String + $this: VALUE_PARAMETER name: type:foo.Derived + BLOCK_BODY + CALL 'protected open fun foo (): kotlin.Unit declared in foo.Derived' type=kotlin.Unit origin=null + $this: GET_VAR ': foo.Derived declared in foo.Derived.box' type=foo.Derived origin=null + RETURN type=kotlin.Nothing from='public final fun box (): kotlin.String declared in foo.Derived' + CALL 'public final fun (): kotlin.String declared in foo.A' type=kotlin.String origin=GET_PROPERTY + $this: TYPE_OP type=foo.A origin=IMPLICIT_NOTNULL typeOperand=foo.A + GET_FIELD 'FIELD IR_EXTERNAL_JAVA_DECLARATION_STUB name:a type:@[FlexibleNullability] foo.A? visibility:protected/*protected and package*/' type=@[FlexibleNullability] foo.A? origin=GET_PROPERTY + receiver: GET_VAR ': foo.Derived declared in foo.Derived.box' type=foo.Derived origin=null + PROPERTY FAKE_OVERRIDE name:a visibility:protected/*protected and package*/ modality:FINAL [fake_override,var] + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [fake_override,operator] declared in bar.Base + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int [fake_override] declared in bar.Base + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String [fake_override] declared in bar.Base + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN name:box visibility:public modality:FINAL <> () returnType:kotlin.String + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun box (): kotlin.String declared in foo' + CALL 'public final fun box (): kotlin.String declared in foo.Derived' type=kotlin.String origin=null + $this: CONSTRUCTOR_CALL 'public constructor () [primary] declared in foo.Derived' type=foo.Derived origin=null diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index b22859eb5d5..92198057c4e 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -15098,6 +15098,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/fir"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("callableReferenceToJavaField.kt") + public void testCallableReferenceToJavaField() throws Exception { + runTest("compiler/testData/codegen/box/fir/callableReferenceToJavaField.kt"); + } + @Test @TestMetadata("callableReferenceToStaticFunction.kt") public void testCallableReferenceToStaticFunction() throws Exception {