From 76d3c0e8047d65c23e66692622c813e5fd2e892a Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Fri, 18 Aug 2023 15:30:49 +0300 Subject: [PATCH] [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 --- .../generators/ClassMemberGenerator.kt | 6 +- .../generators/DataClassMembersGenerator.kt | 118 +++++++++++------- .../jetbrains/kotlin/fir/types/ArrayUtils.kt | 25 +++- .../AnnotationImplementationTransformer.kt | 4 + .../ir/util/DataClassMembersGenerator.kt | 12 +- compiler/testData/codegen/box/ir/kt52677.kt | 2 +- 6 files changed, 111 insertions(+), 56 deletions(-) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/ClassMemberGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/ClassMemberGenerator.kt index 9e63c3dc653..700e0e37cdb 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/ClassMemberGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/ClassMemberGenerator.kt @@ -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) } } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DataClassMembersGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DataClassMembersGenerator.kt index 4acfade1651..675de048812 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DataClassMembersGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DataClassMembersGenerator.kt @@ -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 = - MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER) + fun generateSingleFieldValueClassMembers(klass: FirRegularClass, irClass: IrClass): List { + return MyDataClassMethodsGenerator(irClass, klass, IrDeclarationOrigin.GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER) .generate(klass) + } - fun generateMultiFieldValueClassMembers(klass: FirRegularClass, irClass: IrClass): List = - MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER) + fun generateMultiFieldValueClassMembers(klass: FirRegularClass, irClass: IrClass): List { + return MyDataClassMethodsGenerator(irClass, klass, IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER) .generate(klass) + } - fun generateDataClassMembers(klass: FirRegularClass, irClass: IrClass): List = - MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER).generate(klass) + fun generateDataClassMembers(klass: FirRegularClass, irClass: IrClass): List { + 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, diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt index 9b5c6ae3d2a..78e4e09ee77 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt @@ -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) } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt index 0a53fd84a0b..5a97ee4393a 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt @@ -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 diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt index 522b3702a11..c8d78ec51d0 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt @@ -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() diff --git a/compiler/testData/codegen/box/ir/kt52677.kt b/compiler/testData/codegen/box/ir/kt52677.kt index f11bdf056ad..6ee4c1d2a97 100644 --- a/compiler/testData/codegen/box/ir/kt52677.kt +++ b/compiler/testData/codegen/box/ir/kt52677.kt @@ -1,5 +1,5 @@ // ISSUE: KT-52677 - +// IGNORE_BACKEND_K2: ANY // MODULE: lib // FILE: lib.kt