FIR2IR: support static fake overrides for fields (related to KT-53441)

#KT-54921 Fixed
This commit is contained in:
Mikhail Glukhikh
2022-11-08 13:26:16 +01:00
committed by Space Team
parent 6979271998
commit c698d060c6
11 changed files with 162 additions and 43 deletions
@@ -181,8 +181,8 @@ context(Fir2IrComponents)
fun FirReference.toSymbolForCall( fun FirReference.toSymbolForCall(
dispatchReceiver: FirExpression, dispatchReceiver: FirExpression,
conversionScope: Fir2IrConversionScope, conversionScope: Fir2IrConversionScope,
explicitReceiver: FirExpression?,
preferGetter: Boolean = true, preferGetter: Boolean = true,
explicitReceiver: FirExpression? = null,
isDelegate: Boolean = false, isDelegate: Boolean = false,
isReference: Boolean = false isReference: Boolean = false
): IrSymbol? { ): IrSymbol? {
@@ -278,7 +278,7 @@ private fun FirCallableSymbol<*>.toSymbolForCall(
is FirFunctionSymbol<*> -> declarationStorage.getIrFunctionSymbol(this, fakeOverrideOwnerLookupTag) is FirFunctionSymbol<*> -> declarationStorage.getIrFunctionSymbol(this, fakeOverrideOwnerLookupTag)
is FirPropertySymbol -> declarationStorage.getIrPropertySymbol(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 FirBackingFieldSymbol -> declarationStorage.getIrBackingFieldSymbol(this)
is FirDelegateFieldSymbol -> declarationStorage.getIrDelegateFieldSymbol(this) is FirDelegateFieldSymbol -> declarationStorage.getIrDelegateFieldSymbol(this)
is FirVariableSymbol<*> -> declarationStorage.getIrValueSymbol(this) is FirVariableSymbol<*> -> declarationStorage.getIrValueSymbol(this)
@@ -51,6 +51,7 @@ import org.jetbrains.kotlin.ir.types.IrErrorType
import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
@@ -108,6 +109,8 @@ class Fir2IrDeclarationStorage(
private val fieldCache = ConcurrentHashMap<FirField, IrField>() private val fieldCache = ConcurrentHashMap<FirField, IrField>()
private val fieldStaticOverrideCache = ConcurrentHashMap<Pair<Name, ClassId>, IrField>()
private val localStorage by threadLocal { Fir2IrLocalStorage() } private val localStorage by threadLocal { Fir2IrLocalStorage() }
private fun areCompatible(firFunction: FirFunction, irFunction: IrFunction): Boolean { private fun areCompatible(firFunction: FirFunction, irFunction: IrFunction): Boolean {
@@ -1014,7 +1017,22 @@ class Fir2IrDeclarationStorage(
return fakeOverridesInClass[irClass]?.get(callableDeclaration) 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? { fun createIrFieldAndDelegatedMembers(field: FirField, owner: FirClass, irClass: IrClass): IrField? {
// Either take a corresponding constructor property backing field, // Either take a corresponding constructor property backing field,
@@ -1045,22 +1063,24 @@ class Fir2IrDeclarationStorage(
} }
} }
} }
val irField = createIrField( return createIrField(
field, field,
irParent = irClass,
typeRef = initializer?.typeRef ?: field.returnTypeRef, typeRef = initializer?.typeRef ?: field.returnTypeRef,
origin = IrDeclarationOrigin.DELEGATE origin = IrDeclarationOrigin.DELEGATE
) )
irField.setAndModifyParent(irClass)
return irField
} }
private fun createIrField( internal fun createIrField(
field: FirField, field: FirField,
irParent: IrDeclarationParent?,
typeRef: FirTypeRef = field.returnTypeRef, typeRef: FirTypeRef = field.returnTypeRef,
origin: IrDeclarationOrigin = IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB origin: IrDeclarationOrigin = IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
): IrField = convertCatching(field) { ): IrField = convertCatching(field) {
val type = typeRef.toIrType() 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 -> return field.convertWithOffsets { startOffset, endOffset ->
if (signature != null) { if (signature != null) {
symbolTable.declareField( symbolTable.declareField(
@@ -1083,11 +1103,18 @@ class Fir2IrDeclarationStorage(
isStatic = field.isStatic isStatic = field.isStatic
) )
}.apply { }.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 val initializer = field.initializer
if (initializer is FirConstExpression<*>) { if (initializer is FirConstExpression<*>) {
this.initializer = factory.createExpressionBody(initializer.toIrConst(type)) this.initializer = factory.createExpressionBody(initializer.toIrConst(type))
} }
setAndModifyParent(irParent)
} }
} }
} }
@@ -1513,16 +1540,32 @@ class Fir2IrDeclarationStorage(
else -> parentOrigin else -> parentOrigin
} }
fun getIrFieldSymbol(firFieldSymbol: FirFieldSymbol): IrFieldSymbol { fun getIrFieldSymbol(
firFieldSymbol: FirFieldSymbol,
fakeOverrideOwnerLookupTag: ConeClassLikeLookupTag? = null
): IrFieldSymbol {
val fir = firFieldSymbol.fir val fir = firFieldSymbol.fir
val irField = fieldCache[fir] ?: run { val unmatchedOwner = fakeOverrideOwnerLookupTag != null && fakeOverrideOwnerLookupTag != firFieldSymbol.containingClassLookupTag()
// In case of type parameters from the parent as the field's return type, find the parent ahead to cache type parameters. if (!fir.isStatic || !unmatchedOwner) {
val irParent = findIrParent(fir) fieldCache[fir]?.let { return it.symbol }
createIrField(fir).apply { }
setAndModifyParent(irParent)
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 { fun getIrBackingFieldSymbol(firBackingFieldSymbol: FirBackingFieldSymbol): IrSymbol {
@@ -79,7 +79,7 @@ class Fir2IrVisitor(
override fun visitField(field: FirField, data: Any?): IrField { override fun visitField(field: FirField, data: Any?): IrField {
if (field.isSynthetic) { 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, // If this is a property backing field, then it has no separate initializer,
// so we shouldn't convert it // so we shouldn't convert it
if (correspondingPropertySymbol == null) { if (correspondingPropertySymbol == null) {
@@ -557,7 +557,10 @@ class CallAndReferenceGenerator(
} }
val symbol = calleeReference.toSymbolForCall( val symbol = calleeReference.toSymbolForCall(
variableAssignment.dispatchReceiver, conversionScope, preferGetter = false variableAssignment.dispatchReceiver,
conversionScope,
explicitReceiver = variableAssignment.explicitReceiver,
preferGetter = false,
) )
val origin = variableAssignment.getIrAssignmentOrigin() val origin = variableAssignment.getIrAssignmentOrigin()
@@ -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.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol 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.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.isStatic 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.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
@@ -39,6 +41,7 @@ class FakeOverrideGenerator(
private val baseFunctionSymbols = mutableMapOf<IrFunction, List<FirNamedFunctionSymbol>>() private val baseFunctionSymbols = mutableMapOf<IrFunction, List<FirNamedFunctionSymbol>>()
private val basePropertySymbols = mutableMapOf<IrProperty, List<FirPropertySymbol>>() private val basePropertySymbols = mutableMapOf<IrProperty, List<FirPropertySymbol>>()
private val baseStaticFieldSymbols = mutableMapOf<IrField, List<FirFieldSymbol>>()
private fun IrSimpleFunction.withFunction(f: IrSimpleFunction.() -> Unit): IrSimpleFunction { private fun IrSimpleFunction.withFunction(f: IrSimpleFunction.() -> Unit): IrSimpleFunction {
return conversionScope.withFunction(this, f) return conversionScope.withFunction(this, f)
@@ -130,29 +133,61 @@ class FakeOverrideGenerator(
) )
} }
useSiteOrStaticScope.processPropertiesByName(name) { propertySymbol -> useSiteOrStaticScope.processPropertiesByName(name) { propertyOrFieldSymbol ->
createFakeOverriddenIfNeeded( when (propertyOrFieldSymbol) {
firClass, irClass, isLocal, propertySymbol, is FirPropertySymbol -> {
declarationStorage::getCachedIrProperty, createFakeOverriddenIfNeeded(
declarationStorage::createIrProperty, firClass, irClass, isLocal, propertyOrFieldSymbol,
createFakeOverrideSymbol = { firProperty, callableSymbol -> declarationStorage::getCachedIrProperty,
val symbolForOverride = FirFakeOverrideGenerator.createSymbolForSubstitutionOverride(callableSymbol, firClass.symbol.classId) declarationStorage::createIrProperty,
FirFakeOverrideGenerator.createSubstitutionOverrideProperty( createFakeOverrideSymbol = { firProperty, callableSymbol ->
session, symbolForOverride, firProperty, val symbolForOverride =
newDispatchReceiverType = firClass.defaultType(), FirFakeOverrideGenerator.createSymbolForSubstitutionOverride(callableSymbol, firClass.symbol.classId)
isExpect = (firClass as? FirRegularClass)?.isExpect == true 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, is FirFieldSymbol -> {
containsErrorTypes = { irProperty -> if (!propertyOrFieldSymbol.isStatic) return@processPropertiesByName
irProperty.backingField?.type?.containsErrorType() == true || createFakeOverriddenIfNeeded(
irProperty.getter?.returnType?.containsErrorType() == true firClass, irClass, isLocal, propertyOrFieldSymbol,
}, { field, _, _ -> declarationStorage.getCachedIrField(field, staticFakeOverrideOwnerLookupTag = null) },
realDeclarationSymbols, { field, irParent, _, _, _ ->
FirTypeScope::getDirectOverriddenProperties, declarationStorage.createIrField(field, irParent)
useSiteOrStaticScope, },
) 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 -> {
}
}
} }
} }
@@ -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); 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 @Test
@TestMetadata("inheritedPackageStaticFunction.kt") @TestMetadata("inheritedPackageStaticFunction.kt")
public void testInheritedPackageStaticFunction() throws Exception { public void testInheritedPackageStaticFunction() throws Exception {
@@ -310,7 +310,9 @@ class FirClassSubstitutionScope(
// TODO: do we have fields with implicit type? // TODO: do we have fields with implicit type?
val newReturnType = returnType?.substitute() ?: return original 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 { fun createSubstitutionOverrideSyntheticProperty(original: FirSyntheticPropertySymbol): FirSyntheticPropertySymbol {
@@ -487,7 +487,8 @@ object FirFakeOverrideGenerator {
baseField: FirField, baseField: FirField,
baseSymbol: FirFieldSymbol, baseSymbol: FirFieldSymbol,
newReturnType: ConeKotlinType?, newReturnType: ConeKotlinType?,
derivedClassId: ClassId? derivedClassId: ClassId?,
withInitializer: Boolean
): FirFieldSymbol { ): FirFieldSymbol {
val symbol = FirFieldSymbol( val symbol = FirFieldSymbol(
CallableId(derivedClassId ?: baseSymbol.callableId.classId!!, baseField.name) CallableId(derivedClassId ?: baseSymbol.callableId.classId!!, baseField.name)
@@ -507,8 +508,14 @@ object FirFakeOverrideGenerator {
annotations += baseField.annotations annotations += baseField.annotations
attributes = baseField.attributes.copy() attributes = baseField.attributes.copy()
dispatchReceiverType = baseField.dispatchReceiverType dispatchReceiverType = baseField.dispatchReceiverType
if (withInitializer) {
initializer = baseField.initializer
}
}.apply { }.apply {
originalForSubstitutionOverrideAttr = baseField originalForSubstitutionOverrideAttr = baseField
if (isStatic && derivedClassId != null) {
containingClassForStaticMemberAttr = ConeClassLikeLookupTagImpl(derivedClassId)
}
} }
return symbol return symbol
} }
@@ -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
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_K2: JVM_IR
// FILE: Child.java // FILE: Child.java
class Child extends Parent { class Child extends Parent {
@@ -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); 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 @Test
@TestMetadata("inheritedPackageStaticFunction.kt") @TestMetadata("inheritedPackageStaticFunction.kt")
public void testInheritedPackageStaticFunction() throws Exception { public void testInheritedPackageStaticFunction() throws Exception {