[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:
committed by
Space Team
parent
b38de13696
commit
76d3c0e804
+2
-4
@@ -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) }
|
||||
}
|
||||
|
||||
+76
-42
@@ -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)
|
||||
}
|
||||
|
||||
+4
@@ -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
@@ -1,5 +1,5 @@
|
||||
// ISSUE: KT-52677
|
||||
|
||||
// IGNORE_BACKEND_K2: ANY
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
|
||||
|
||||
Reference in New Issue
Block a user