[FIR2IR] Get rid of all usages of IrSymbol.owner from DataClassMembersGenerator

This change uncovered the following problem in pipeline: we have a contract,
  that all bodies will be generated after all fake overrides will be preprocessed.
  But DataClassMembersGenerator generates bodies before f/o creation,
  which leads to the problem, if data class has forward reference to some
  class which was not processed before (because in this case generator
  of `hashCode` function will try to reference f/o, which is not created yet,
  which leads to `SymbolAlreadyBound` problem)

```
data class Some(val a: A) {
    generated fun hashCode(): Int {
        return a.hashCode() // (1) is not generated yet
    }
}

class A {
    fake-override fun hashCode(): Int // (1)
}
```

This problem will be fixed in the next commit

(related test is compiler/testData/codegen/box/ir/kt52677.kt)

^KT-60924
This commit is contained in:
Dmitriy Novozhilov
2023-08-18 15:30:49 +03:00
committed by Space Team
parent b38de13696
commit 76d3c0e804
6 changed files with 111 additions and 56 deletions
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertySetter
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.isFromEnumClass
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.dispatchReceiverClassLookupTagOrNull
import org.jetbrains.kotlin.fir.expressions.FirDelegatedConstructorCall
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
@@ -175,14 +174,13 @@ internal class ClassMemberGenerator(
irFunction.body = IrSyntheticBodyImpl(startOffset, endOffset, kind)
}
irFunction.parent is IrClass && irFunction.parentAsClass.isData -> {
val lookupTag = firFunction?.symbol?.dispatchReceiverClassLookupTagOrNull()
when {
DataClassResolver.isComponentLike(irFunction.name) ->
firFunction?.body?.let { irFunction.body = visitor.convertToIrBlockBody(it) }
?: DataClassMembersGenerator(components).generateDataClassComponentBody(irFunction, lookupTag!!)
?: DataClassMembersGenerator(components).generateDataClassComponentBody(irFunction, containingClass as FirRegularClass)
DataClassResolver.isCopy(irFunction.name) ->
firFunction?.body?.let { irFunction.body = visitor.convertToIrBlockBody(it) }
?: DataClassMembersGenerator(components).generateDataClassCopyBody(irFunction, lookupTag!!)
?: DataClassMembersGenerator(components).generateDataClassCopyBody(irFunction, containingClass as FirRegularClass)
else ->
irFunction.body = firFunction?.body?.let { visitor.convertToIrBlockBody(it) }
}
@@ -6,37 +6,44 @@
package org.jetbrains.kotlin.fir.backend.generators
import org.jetbrains.kotlin.builtins.StandardNames.HASHCODE_NAME
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
import org.jetbrains.kotlin.fir.backend.Fir2IrComponents
import org.jetbrains.kotlin.fir.backend.FirMetadataSource
import org.jetbrains.kotlin.fir.backend.declareThisReceiverParameter
import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.declarations.utils.fromPrimaryConstructor
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.scopes.getFunctions
import org.jetbrains.kotlin.fir.scopes.getProperties
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isArrayOrPrimitiveArray
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
import org.jetbrains.kotlin.fir.types.toSymbol
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.IrGeneratorContextBase
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbolInternals
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.DataClassMembersGenerator
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.DataClassResolver
import org.jetbrains.kotlin.util.OperatorNameConventions.EQUALS
import org.jetbrains.kotlin.util.OperatorNameConventions.TO_STRING
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
/**
* A generator that generates synthetic members of data class as well as part of inline class.
@@ -48,31 +55,35 @@ import org.jetbrains.kotlin.util.OperatorNameConventions.TO_STRING
*/
class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrComponents by components {
fun generateSingleFieldValueClassMembers(klass: FirRegularClass, irClass: IrClass): List<FirDeclaration> =
MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER)
fun generateSingleFieldValueClassMembers(klass: FirRegularClass, irClass: IrClass): List<FirDeclaration> {
return MyDataClassMethodsGenerator(irClass, klass, IrDeclarationOrigin.GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER)
.generate(klass)
}
fun generateMultiFieldValueClassMembers(klass: FirRegularClass, irClass: IrClass): List<FirDeclaration> =
MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER)
fun generateMultiFieldValueClassMembers(klass: FirRegularClass, irClass: IrClass): List<FirDeclaration> {
return MyDataClassMethodsGenerator(irClass, klass, IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER)
.generate(klass)
}
fun generateDataClassMembers(klass: FirRegularClass, irClass: IrClass): List<FirDeclaration> =
MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER).generate(klass)
fun generateDataClassMembers(klass: FirRegularClass, irClass: IrClass): List<FirDeclaration> {
return MyDataClassMethodsGenerator(irClass, klass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER).generate(klass)
}
fun generateDataClassComponentBody(irFunction: IrFunction, lookupTag: ConeClassLikeLookupTag) =
MyDataClassMethodsGenerator(irFunction.parentAsClass, lookupTag, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
fun generateDataClassComponentBody(irFunction: IrFunction, klass: FirRegularClass) {
MyDataClassMethodsGenerator(irFunction.parentAsClass, klass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
.generateComponentBody(irFunction)
}
fun generateDataClassCopyBody(irFunction: IrFunction, lookupTag: ConeClassLikeLookupTag) =
MyDataClassMethodsGenerator(irFunction.parentAsClass, lookupTag, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
fun generateDataClassCopyBody(irFunction: IrFunction, klass: FirRegularClass) {
MyDataClassMethodsGenerator(irFunction.parentAsClass, klass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
.generateCopyBody(irFunction)
}
private inner class MyDataClassMethodsGenerator(
val irClass: IrClass,
val lookupTag: ConeClassLikeLookupTag,
val klass: FirRegularClass,
val origin: IrDeclarationOrigin
) {
@OptIn(IrSymbolInternals::class)
private val irDataClassMembersGenerator = object : IrBasedDataClassMembersGenerator(
IrGeneratorContextBase(components.irBuiltIns),
components.symbolTable,
@@ -99,39 +110,62 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrCompon
}
}
private fun getHashCodeFunction(klass: IrClass): IrSimpleFunctionSymbol =
klass.functions.singleOrNull {
it.name.asString() == "hashCode" && it.valueParameters.isEmpty() && it.extensionReceiverParameter == null
}?.symbol
?: context.irBuiltIns.anyClass.functions.single { it.owner.name.asString() == "hashCode" }
private fun getHashCodeFunction(klass: FirRegularClass): FirNamedFunctionSymbol {
if (klass.classId == StandardClassIds.Nothing) {
// scope of kotlin.Nothing is empty, so we need to search for `hashCode` in scope of kotlin.Any
return getHashCodeFunction(session.builtinTypes.anyType.type.toRegularClassSymbol(session)!!.fir)
}
val scope = klass.symbol.unsubstitutedScope(
session, scopeSession, withForcedTypeCalculator = false, memberRequiredPhase = null
)
return scope.getFunctions(HASHCODE_NAME).first { symbol ->
val function = symbol.fir
function.valueParameters.isEmpty() && function.receiverParameter == null && function.contextReceivers.isEmpty()
}
}
val IrTypeParameter.erasedUpperBound: IrClass
@Suppress("RecursivePropertyAccessor")
val FirTypeParameter.erasedUpperBound: FirRegularClass
get() {
// Pick the (necessarily unique) non-interface upper bound if it exists
for (type in superTypes) {
val irClass = type.classOrNull?.owner ?: continue
if (!irClass.isInterface && !irClass.isAnnotationClass) return irClass
for (type in bounds) {
val klass = type.toRegularClassSymbol(session)?.fir ?: continue
val kind = klass.classKind
if (kind != ClassKind.INTERFACE && kind != ClassKind.ANNOTATION_CLASS) return klass
}
// Otherwise, choose either the first IrClass supertype or recurse.
// In the first case, all supertypes are interface types and the choice was arbitrary.
// In the second case, there is only a single supertype.
return when (val firstSuper = superTypes.first().classifierOrNull?.owner) {
is IrClass -> firstSuper
is IrTypeParameter -> firstSuper.erasedUpperBound
return when (val firstSuper = bounds.first().coneType.fullyExpandedType(session).toSymbol(session)?.fir) {
is FirRegularClass -> firstSuper
is FirTypeParameter -> firstSuper.erasedUpperBound
else -> error("unknown supertype kind $firstSuper")
}
}
override fun getHashCodeFunctionInfo(type: IrType): HashCodeFunctionInfo {
val classifier = type.classifierOrNull
shouldNotBeCalled()
}
override fun getHashCodeFunctionInfo(property: IrProperty): HashCodeFunctionInfo {
val firProperty = klass.symbol.declaredMemberScope(session, memberRequiredPhase = null)
.getProperties(property.name)
.first { (it as FirPropertySymbol).fromPrimaryConstructor } as FirPropertySymbol
val type = firProperty.resolvedReturnType.fullyExpandedType(session)
val symbol = when {
classifier.isArrayOrPrimitiveArray -> context.irBuiltIns.dataClassArrayMemberHashCodeSymbol
classifier is IrClassSymbol -> getHashCodeFunction(classifier.owner)
classifier is IrTypeParameterSymbol -> getHashCodeFunction(classifier.owner.erasedUpperBound)
else -> error("Unknown classifier kind $classifier")
type.isArrayOrPrimitiveArray(checkUnsignedArrays = false) -> context.irBuiltIns.dataClassArrayMemberHashCodeSymbol
else -> {
val classForType = when (val classifier = type.toSymbol(session)?.fir) {
is FirRegularClass -> classifier
is FirTypeParameter -> classifier.erasedUpperBound
else -> error("Unknown classifier kind $classifier")
}
val firHashCode = getHashCodeFunction(classForType)
val lookupTag = classForType.symbol.toLookupTag()
declarationStorage.getIrFunctionSymbol(firHashCode, lookupTag) as IrSimpleFunctionSymbol
}
}
return Fir2IrHashCodeFunctionInfo(symbol)
}
@@ -234,7 +268,7 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrCompon
otherParameterNeeded: Boolean = false,
isOperator: Boolean = false,
): IrFunction {
val signature = if (lookupTag.classId.isLocal) null else components.signatureComposer.composeSignature(syntheticCounterpart)
val signature = if (klass.symbol.classId.isLocal) null else components.signatureComposer.composeSignature(syntheticCounterpart)
return components.declarationStorage.declareIrSimpleFunction(signature) { symbol ->
components.irFactory.createSimpleFunction(
startOffset = UNDEFINED_OFFSET,
@@ -6,9 +6,14 @@
package org.jetbrains.kotlin.fir.types
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.utils.addToStdlib.runIf
val ConeKotlinType.isArrayOrPrimitiveArray: Boolean
get() = arrayElementType() != null
get() = arrayElementTypeArgument() != null
fun ConeKotlinType.isArrayOrPrimitiveArray(checkUnsignedArrays: Boolean): Boolean {
return arrayElementTypeArgument(checkUnsignedArrays) != null
}
fun ConeKotlinType.createOutArrayType(nullable: Boolean = false, createPrimitiveArrayType: Boolean = true): ConeKotlinType {
return ConeKotlinTypeProjectionOut(this).createArrayType(nullable, createPrimitiveArrayType)
@@ -30,13 +35,23 @@ fun ConeTypeProjection.createArrayType(nullable: Boolean = false, createPrimitiv
return StandardClassIds.Array.constructClassLikeType(arrayOf(this), nullable)
}
fun ConeKotlinType.arrayElementType(): ConeKotlinType? {
fun ConeKotlinType.arrayElementType(checkUnsignedArrays: Boolean = true): ConeKotlinType? {
return when (val argument = arrayElementTypeArgument(checkUnsignedArrays)) {
is ConeKotlinTypeProjection -> argument.type
else -> null
}
}
private fun ConeKotlinType.arrayElementTypeArgument(checkUnsignedArrays: Boolean = true): ConeTypeProjection? {
val type = this.lowerBoundIfFlexible()
if (type !is ConeClassLikeType) return null
val classId = type.lookupTag.classId
if (classId == StandardClassIds.Array)
return (type.typeArguments.first() as ConeKotlinTypeProjection).type
val elementType = StandardClassIds.elementTypeByPrimitiveArrayType[classId] ?: StandardClassIds.elementTypeByUnsignedArrayType[classId]
if (classId == StandardClassIds.Array) {
return type.typeArguments.first()
}
val elementType = StandardClassIds.elementTypeByPrimitiveArrayType[classId] ?: runIf(checkUnsignedArrays) {
StandardClassIds.elementTypeByUnsignedArrayType[classId]
}
if (elementType != null) {
return elementType.constructClassLikeType(emptyArray(), isNullable = false)
}
@@ -272,6 +272,10 @@ class AnnotationImplementationMemberGenerator(
return irCallOp(context.irBuiltIns.intXorSymbol, context.irBuiltIns.intType, multiplied, propertyValueHashCode)
}
private fun IrBuilderWithScope.getHashCodeOf(type: IrType, irValue: IrExpression): IrExpression {
return getHashCodeOf(getHashCodeFunctionInfo(type), irValue)
}
// Manual implementation of equals is required for following reasons:
// 1. `other` should be casted to interface instead of implementation
// 2. Properties should be retrieved using getters without accessing backing fields
@@ -234,11 +234,11 @@ abstract class DataClassMembersGenerator(
protected open fun IrBuilderWithScope.shiftResultOfHashCode(irResultVar: IrVariable): IrExpression =
irCallOp(context.irBuiltIns.intTimesSymbol, context.irBuiltIns.intType, irGet(irResultVar), irInt(31))
protected open fun getHashCodeOf(builder: IrBuilderWithScope, property: IrProperty, irValue: IrExpression) =
builder.getHashCodeOf(property.type, irValue)
protected open fun getHashCodeOf(builder: IrBuilderWithScope, property: IrProperty, irValue: IrExpression): IrExpression {
return builder.getHashCodeOf(getHashCodeFunctionInfo(property), irValue)
}
protected fun IrBuilderWithScope.getHashCodeOf(type: IrType, irValue: IrExpression): IrExpression {
val hashCodeFunctionInfo = getHashCodeFunctionInfo(type)
protected fun IrBuilderWithScope.getHashCodeOf(hashCodeFunctionInfo: HashCodeFunctionInfo, irValue: IrExpression): IrExpression {
val hashCodeFunctionSymbol = hashCodeFunctionInfo.symbol
val hasDispatchReceiver = hashCodeFunctionSymbol.hasDispatchReceiver()
return irCall(
@@ -266,6 +266,10 @@ abstract class DataClassMembersGenerator(
fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression<*>)
}
open fun getHashCodeFunctionInfo(property: IrProperty): HashCodeFunctionInfo {
return getHashCodeFunctionInfo(property.type)
}
abstract fun getHashCodeFunctionInfo(type: IrType): HashCodeFunctionInfo
open fun IrClass.classNameForToString(): String = irClass.name.asString()
+1 -1
View File
@@ -1,5 +1,5 @@
// ISSUE: KT-52677
// IGNORE_BACKEND_K2: ANY
// MODULE: lib
// FILE: lib.kt