diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt index 4e94d804ad8..3715ed0943f 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt @@ -181,8 +181,8 @@ context(Fir2IrComponents) fun FirReference.toSymbolForCall( dispatchReceiver: FirExpression, conversionScope: Fir2IrConversionScope, + explicitReceiver: FirExpression?, preferGetter: Boolean = true, - explicitReceiver: FirExpression? = null, isDelegate: Boolean = false, isReference: Boolean = false ): IrSymbol? { @@ -278,7 +278,7 @@ private fun FirCallableSymbol<*>.toSymbolForCall( is FirFunctionSymbol<*> -> declarationStorage.getIrFunctionSymbol(this, fakeOverrideOwnerLookupTag) is FirPropertySymbol -> declarationStorage.getIrPropertySymbol(this, fakeOverrideOwnerLookupTag) - is FirFieldSymbol -> declarationStorage.getIrFieldSymbol(this) + is FirFieldSymbol -> declarationStorage.getIrFieldSymbol(this, fakeOverrideOwnerLookupTag) is FirBackingFieldSymbol -> declarationStorage.getIrBackingFieldSymbol(this) is FirDelegateFieldSymbol -> declarationStorage.getIrDelegateFieldSymbol(this) is FirVariableSymbol<*> -> declarationStorage.getIrValueSymbol(this) 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 5bda0330da5..ea1efb0f4fa 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 @@ -51,6 +51,7 @@ import org.jetbrains.kotlin.ir.types.IrErrorType import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames @@ -108,6 +109,8 @@ class Fir2IrDeclarationStorage( private val fieldCache = ConcurrentHashMap() + private val fieldStaticOverrideCache = ConcurrentHashMap, IrField>() + private val localStorage by threadLocal { Fir2IrLocalStorage() } private fun areCompatible(firFunction: FirFunction, irFunction: IrFunction): Boolean { @@ -1014,7 +1017,22 @@ class Fir2IrDeclarationStorage( return fakeOverridesInClass[irClass]?.get(callableDeclaration) } - fun getCachedIrField(field: FirField): IrField? = fieldCache[field] + fun getCachedIrDelegateOrBackingField(field: FirField): IrField? = fieldCache[field] + + fun getCachedIrField( + field: FirField, + // Always should be null for non-static + // For static null means "take containing class instead" + staticFakeOverrideOwnerLookupTag: ConeClassLikeLookupTag?, + ): IrField? { + val classId = (staticFakeOverrideOwnerLookupTag ?: field.containingClassLookupTag())?.classId + if (classId == null || !field.isStatic || + !field.isSubstitutionOrIntersectionOverride && staticFakeOverrideOwnerLookupTag == field.containingClassLookupTag() + ) { + return fieldCache[field] + } + return fieldStaticOverrideCache[field.name to classId] + } fun createIrFieldAndDelegatedMembers(field: FirField, owner: FirClass, irClass: IrClass): IrField? { // Either take a corresponding constructor property backing field, @@ -1045,22 +1063,24 @@ class Fir2IrDeclarationStorage( } } } - val irField = createIrField( + return createIrField( field, + irParent = irClass, typeRef = initializer?.typeRef ?: field.returnTypeRef, origin = IrDeclarationOrigin.DELEGATE ) - irField.setAndModifyParent(irClass) - return irField } - private fun createIrField( + internal fun createIrField( field: FirField, + irParent: IrDeclarationParent?, typeRef: FirTypeRef = field.returnTypeRef, origin: IrDeclarationOrigin = IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB ): IrField = convertCatching(field) { val type = typeRef.toIrType() - val signature = signatureComposer.composeSignature(field) + val classId = (irParent as? IrClass)?.classId + val containingClass = classId?.let { ConeClassLikeLookupTagImpl(it) } + val signature = signatureComposer.composeSignature(field, containingClass) return field.convertWithOffsets { startOffset, endOffset -> if (signature != null) { symbolTable.declareField( @@ -1083,11 +1103,18 @@ class Fir2IrDeclarationStorage( isStatic = field.isStatic ) }.apply { - fieldCache[field] = this + if (classId == null || !field.isStatic || + !field.isSubstitutionOrIntersectionOverride && classId == field.containingClassLookupTag()?.classId + ) { + fieldCache[field] = this + } else { + fieldStaticOverrideCache[field.name to classId] = this + } val initializer = field.initializer if (initializer is FirConstExpression<*>) { this.initializer = factory.createExpressionBody(initializer.toIrConst(type)) } + setAndModifyParent(irParent) } } } @@ -1513,16 +1540,32 @@ class Fir2IrDeclarationStorage( else -> parentOrigin } - fun getIrFieldSymbol(firFieldSymbol: FirFieldSymbol): IrFieldSymbol { + fun getIrFieldSymbol( + firFieldSymbol: FirFieldSymbol, + fakeOverrideOwnerLookupTag: ConeClassLikeLookupTag? = null + ): IrFieldSymbol { val fir = firFieldSymbol.fir - val irField = fieldCache[fir] ?: run { - // In case of type parameters from the parent as the field's return type, find the parent ahead to cache type parameters. - val irParent = findIrParent(fir) - createIrField(fir).apply { - setAndModifyParent(irParent) + val unmatchedOwner = fakeOverrideOwnerLookupTag != null && fakeOverrideOwnerLookupTag != firFieldSymbol.containingClassLookupTag() + if (!fir.isStatic || !unmatchedOwner) { + fieldCache[fir]?.let { return it.symbol } + } + + if (fir.isStatic) { + generateLazyFakeOverrides(fir.name, fakeOverrideOwnerLookupTag) + if (fakeOverrideOwnerLookupTag != null && fakeOverrideOwnerLookupTag !is ConeClassLookupTagWithFixedSymbol) { + getCachedIrField(fir, fakeOverrideOwnerLookupTag)?.let { + return it.symbol + } } } - return irField.symbol + // In case of type parameters from the parent as the field's return type, find the parent ahead to cache type parameters. + val irParent = findIrParent(fir) + + val unwrapped = fir.unwrapFakeOverrides() + if (unwrapped !== fir) { + return getIrFieldSymbol(unwrapped.symbol) + } + return createIrField(fir, irParent).symbol } fun getIrBackingFieldSymbol(firBackingFieldSymbol: FirBackingFieldSymbol): IrSymbol { diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt index cc39aeeba03..da77f5593cc 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt @@ -79,7 +79,7 @@ class Fir2IrVisitor( override fun visitField(field: FirField, data: Any?): IrField { if (field.isSynthetic) { - return declarationStorage.getCachedIrField(field)!!.apply { + return declarationStorage.getCachedIrDelegateOrBackingField(field)!!.apply { // If this is a property backing field, then it has no separate initializer, // so we shouldn't convert it if (correspondingPropertySymbol == null) { 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 c75fbb60bd8..9f0496f86b8 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 @@ -557,7 +557,10 @@ class CallAndReferenceGenerator( } val symbol = calleeReference.toSymbolForCall( - variableAssignment.dispatchReceiver, conversionScope, preferGetter = false + variableAssignment.dispatchReceiver, + conversionScope, + explicitReceiver = variableAssignment.explicitReceiver, + preferGetter = false, ) val origin = variableAssignment.getIrAssignmentOrigin() diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/FakeOverrideGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/FakeOverrideGenerator.kt index 7374ce6f28c..9c64bfbde1f 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/FakeOverrideGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/FakeOverrideGenerator.kt @@ -19,9 +19,11 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirFakeOverrideGenerator import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirFieldSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.isStatic +import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol @@ -39,6 +41,7 @@ class FakeOverrideGenerator( private val baseFunctionSymbols = mutableMapOf>() private val basePropertySymbols = mutableMapOf>() + private val baseStaticFieldSymbols = mutableMapOf>() private fun IrSimpleFunction.withFunction(f: IrSimpleFunction.() -> Unit): IrSimpleFunction { return conversionScope.withFunction(this, f) @@ -130,29 +133,61 @@ class FakeOverrideGenerator( ) } - useSiteOrStaticScope.processPropertiesByName(name) { propertySymbol -> - createFakeOverriddenIfNeeded( - firClass, irClass, isLocal, propertySymbol, - declarationStorage::getCachedIrProperty, - declarationStorage::createIrProperty, - createFakeOverrideSymbol = { firProperty, callableSymbol -> - val symbolForOverride = FirFakeOverrideGenerator.createSymbolForSubstitutionOverride(callableSymbol, firClass.symbol.classId) - FirFakeOverrideGenerator.createSubstitutionOverrideProperty( - session, symbolForOverride, firProperty, - newDispatchReceiverType = firClass.defaultType(), - isExpect = (firClass as? FirRegularClass)?.isExpect == true + useSiteOrStaticScope.processPropertiesByName(name) { propertyOrFieldSymbol -> + when (propertyOrFieldSymbol) { + is FirPropertySymbol -> { + createFakeOverriddenIfNeeded( + firClass, irClass, isLocal, propertyOrFieldSymbol, + declarationStorage::getCachedIrProperty, + declarationStorage::createIrProperty, + createFakeOverrideSymbol = { firProperty, callableSymbol -> + val symbolForOverride = + FirFakeOverrideGenerator.createSymbolForSubstitutionOverride(callableSymbol, firClass.symbol.classId) + FirFakeOverrideGenerator.createSubstitutionOverrideProperty( + session, symbolForOverride, firProperty, + newDispatchReceiverType = firClass.defaultType(), + isExpect = (firClass as? FirRegularClass)?.isExpect == true + ) + }, + basePropertySymbols, + result, + containsErrorTypes = { irProperty -> + irProperty.backingField?.type?.containsErrorType() == true || + irProperty.getter?.returnType?.containsErrorType() == true + }, + realDeclarationSymbols, + FirTypeScope::getDirectOverriddenProperties, + useSiteOrStaticScope, ) - }, - basePropertySymbols, - result, - containsErrorTypes = { irProperty -> - irProperty.backingField?.type?.containsErrorType() == true || - irProperty.getter?.returnType?.containsErrorType() == true - }, - realDeclarationSymbols, - FirTypeScope::getDirectOverriddenProperties, - useSiteOrStaticScope, - ) + } + + is FirFieldSymbol -> { + if (!propertyOrFieldSymbol.isStatic) return@processPropertiesByName + createFakeOverriddenIfNeeded( + firClass, irClass, isLocal, propertyOrFieldSymbol, + { field, _, _ -> declarationStorage.getCachedIrField(field, staticFakeOverrideOwnerLookupTag = null) }, + { field, irParent, _, _, _ -> + declarationStorage.createIrField(field, irParent) + }, + createFakeOverrideSymbol = { firField, callableSymbol -> + FirFakeOverrideGenerator.createSubstitutionOverrideField( + session, firField, callableSymbol, + newReturnType = firField.returnTypeRef.coneType, + firClass.symbol.classId, withInitializer = true + ) + }, + baseStaticFieldSymbols, + result, + containsErrorTypes = { irField -> irField.type.containsErrorType() }, + realDeclarationSymbols, + computeDirectOverridden = { emptyList() }, + useSiteOrStaticScope, + ) + } + + else -> { + } + } } } 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 2a20ccd5129..44923a6ab5e 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 @@ -28782,6 +28782,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/javaVisibility/package"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("inheritedPackageStaticField.kt") + public void testInheritedPackageStaticField() throws Exception { + runTest("compiler/testData/codegen/box/javaVisibility/package/inheritedPackageStaticField.kt"); + } + @Test @TestMetadata("inheritedPackageStaticFunction.kt") public void testInheritedPackageStaticFunction() throws Exception { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassSubstitutionScope.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassSubstitutionScope.kt index 2ed3f63b214..f5911fe6224 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassSubstitutionScope.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassSubstitutionScope.kt @@ -310,7 +310,9 @@ class FirClassSubstitutionScope( // TODO: do we have fields with implicit type? val newReturnType = returnType?.substitute() ?: return original - return FirFakeOverrideGenerator.createSubstitutionOverrideField(session, member, original, newReturnType, newOwnerClassId) + return FirFakeOverrideGenerator.createSubstitutionOverrideField( + session, member, original, newReturnType, newOwnerClassId, withInitializer = false + ) } fun createSubstitutionOverrideSyntheticProperty(original: FirSyntheticPropertySymbol): FirSyntheticPropertySymbol { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirFakeOverrideGenerator.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirFakeOverrideGenerator.kt index 37704bf798a..ee4486d86c1 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirFakeOverrideGenerator.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirFakeOverrideGenerator.kt @@ -487,7 +487,8 @@ object FirFakeOverrideGenerator { baseField: FirField, baseSymbol: FirFieldSymbol, newReturnType: ConeKotlinType?, - derivedClassId: ClassId? + derivedClassId: ClassId?, + withInitializer: Boolean ): FirFieldSymbol { val symbol = FirFieldSymbol( CallableId(derivedClassId ?: baseSymbol.callableId.classId!!, baseField.name) @@ -507,8 +508,14 @@ object FirFakeOverrideGenerator { annotations += baseField.annotations attributes = baseField.attributes.copy() dispatchReceiverType = baseField.dispatchReceiverType + if (withInitializer) { + initializer = baseField.initializer + } }.apply { originalForSubstitutionOverrideAttr = baseField + if (isStatic && derivedClassId != null) { + containingClassForStaticMemberAttr = ConeClassLikeLookupTagImpl(derivedClassId) + } } return symbol } diff --git a/compiler/testData/codegen/box/javaVisibility/package/inheritedPackageStaticField.kt b/compiler/testData/codegen/box/javaVisibility/package/inheritedPackageStaticField.kt new file mode 100644 index 00000000000..0d417d2821d --- /dev/null +++ b/compiler/testData/codegen/box/javaVisibility/package/inheritedPackageStaticField.kt @@ -0,0 +1,18 @@ +// TARGET_BACKEND: JVM_IR +// ISSUE: KT-53441 +// MODULE: lib +// FILE: test/J.java +package test; + +interface I { + // Not a String to avoid constant inlining + public static String[] OK = new String[]{"OK"}; +} + +public class J implements I {} + +// MODULE: main(lib) +// FILE: k.kt +import test.J + +fun box() = J.OK[0] // accessible by JVM rules as J.OK, but not I.OK diff --git a/compiler/testData/codegen/bytecodeText/javaStatics.kt b/compiler/testData/codegen/bytecodeText/javaStatics.kt index 3e30d3dea0f..112c6ed69ba 100644 --- a/compiler/testData/codegen/bytecodeText/javaStatics.kt +++ b/compiler/testData/codegen/bytecodeText/javaStatics.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_K2: JVM_IR // FILE: Child.java class Child extends Parent { 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 cea5910bc65..06b7794f4f3 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 @@ -28782,6 +28782,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/javaVisibility/package"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("inheritedPackageStaticField.kt") + public void testInheritedPackageStaticField() throws Exception { + runTest("compiler/testData/codegen/box/javaVisibility/package/inheritedPackageStaticField.kt"); + } + @Test @TestMetadata("inheritedPackageStaticFunction.kt") public void testInheritedPackageStaticFunction() throws Exception {