From f48142a6e6547fa9741b8a9feb2629ba7f17b34a Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Fri, 12 May 2023 09:47:12 +0200 Subject: [PATCH] FIR2IR: fix binding of data class generated members Related to KT-54844 #KT-54952 Fixed #KT-54887 Fixed --- .../generators/DataClassMembersGenerator.kt | 124 ++++-------------- .../equalsHashCodeToString.kt | 1 - .../equalsHashCodeToStringGeneric.kt | 1 - 3 files changed, 25 insertions(+), 101 deletions(-) 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 6de0b518f58..98716275e60 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 @@ -10,27 +10,11 @@ import org.jetbrains.kotlin.descriptors.* 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.backend.toIrType -import org.jetbrains.kotlin.fir.declarations.FirClass -import org.jetbrains.kotlin.fir.declarations.FirDeclaration -import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin -import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction -import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunction -import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter -import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl -import org.jetbrains.kotlin.fir.declarations.utils.isExpect -import org.jetbrains.kotlin.fir.declarations.utils.modality -import org.jetbrains.kotlin.fir.moduleData +import org.jetbrains.kotlin.fir.containingClassLookupTag +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.render 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.FirValueParameterSymbol -import org.jetbrains.kotlin.fir.types.ConeStarProjection -import org.jetbrains.kotlin.fir.types.constructType -import org.jetbrains.kotlin.fir.types.impl.FirImplicitBooleanTypeRef -import org.jetbrains.kotlin.fir.types.impl.FirImplicitIntTypeRef -import org.jetbrains.kotlin.fir.types.impl.FirImplicitNullableAnyTypeRef -import org.jetbrains.kotlin.fir.types.impl.FirImplicitStringTypeRef import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.IrGeneratorContextBase import org.jetbrains.kotlin.ir.declarations.* @@ -44,7 +28,6 @@ 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.CallableId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DataClassResolver import org.jetbrains.kotlin.util.OperatorNameConventions.EQUALS @@ -60,15 +43,15 @@ import org.jetbrains.kotlin.util.OperatorNameConventions.TO_STRING */ class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrComponents by components { - fun generateSingleFieldValueClassMembers(klass: FirClass, irClass: IrClass): List = + fun generateSingleFieldValueClassMembers(klass: FirRegularClass, irClass: IrClass): List = MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER) .generate(klass) - fun generateMultiFieldValueClassMembers(klass: FirClass, irClass: IrClass): List = + fun generateMultiFieldValueClassMembers(klass: FirRegularClass, irClass: IrClass): List = MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER) .generate(klass) - fun generateDataClassMembers(klass: FirClass, irClass: IrClass): List = + fun generateDataClassMembers(klass: FirRegularClass, irClass: IrClass): List = MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER).generate(klass) fun generateDataClassComponentBody(irFunction: IrFunction, lookupTag: ConeClassLikeLookupTag) = @@ -163,64 +146,41 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrCompon UNDEFINED_OFFSET ) - - private val FirSimpleFunction.matchesEqualsSignature: Boolean - get() = valueParameters.size == 1 && - valueParameters[0].returnTypeRef.toIrType(components.typeConverter) == components.irBuiltIns.anyNType && - returnTypeRef.toIrType(components.typeConverter) == components.irBuiltIns.booleanType - - private val FirSimpleFunction.matchesHashCodeSignature: Boolean - get() = valueParameters.isEmpty() && - returnTypeRef.toIrType(components.typeConverter) == components.irBuiltIns.intType - - private val FirSimpleFunction.matchesToStringSignature: Boolean - get() = valueParameters.isEmpty() && - returnTypeRef.toIrType(components.typeConverter) == components.irBuiltIns.stringType - - private val FirSimpleFunction.matchesDataClassSyntheticMemberSignatures: Boolean - get() = (this.name == EQUALS && matchesEqualsSignature) || - (this.name == HASHCODE_NAME && matchesHashCodeSignature) || - (this.name == TO_STRING && matchesToStringSignature) - - fun generate(klass: FirClass): List { + fun generate(klass: FirRegularClass): List { val propertyParametersCount = irClass.primaryConstructor?.explicitParameters?.size ?: 0 val properties = irClass.properties.filter { it.backingField != null }.take(propertyParametersCount).toList() val result = mutableListOf() - val contributedFunctionsInThisType = klass.declarations.mapNotNull { - if (it is FirSimpleFunction && it.matchesDataClassSyntheticMemberSignatures) { - it.name - } else - null - } val scope = klass.unsubstitutedScope( components.session, components.scopeSession, withForcedTypeCalculator = true, memberRequiredPhase = null, ) - val contributedFunctionsInSupertypes = + val contributedSyntheticFunctions = buildMap { for (name in listOf(EQUALS, HASHCODE_NAME, TO_STRING)) { - // We won't synthesize a function if there is a user-contributed one. - if (contributedFunctionsInThisType.contains(name)) continue scope.processFunctionsByName(name) { - val declaration = it.fir - if (declaration.matchesDataClassSyntheticMemberSignatures && declaration.modality != Modality.FINAL) { - putIfAbsent(declaration.name, declaration) + // We won't synthesize a function if there is a user-contributed (non-synthetic) one. + if (it.origin != FirDeclarationOrigin.Synthetic) return@processFunctionsByName + if (it.containingClassLookupTag() != klass.symbol.toLookupTag()) return@processFunctionsByName + require(!contains(name)) { + "Two synthetic functions $name were found in data/value class ${klass.name}:\n" + + "${this[name]?.render()}\n${it.fir.render()}" } + this[name] = it.fir } } } - val equalsContributedFunction = contributedFunctionsInSupertypes[EQUALS] + val equalsContributedFunction = contributedSyntheticFunctions[EQUALS] if (equalsContributedFunction != null) { result.add(equalsContributedFunction) val equalsFunction = createSyntheticIrFunction( EQUALS, + equalsContributedFunction, components.irBuiltIns.booleanType, - isExpect = klass.isExpect, otherParameterNeeded = true, isOperator = true ) @@ -228,25 +188,25 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrCompon irClass.declarations.add(equalsFunction) } - val hashcodeNameContributedFunction = contributedFunctionsInSupertypes[HASHCODE_NAME] + val hashcodeNameContributedFunction = contributedSyntheticFunctions[HASHCODE_NAME] if (hashcodeNameContributedFunction != null) { result.add(hashcodeNameContributedFunction) val hashCodeFunction = createSyntheticIrFunction( HASHCODE_NAME, + hashcodeNameContributedFunction, components.irBuiltIns.intType, - isExpect = klass.isExpect ) irDataClassMembersGenerator.generateHashCodeMethod(hashCodeFunction, properties) irClass.declarations.add(hashCodeFunction) } - val toStringContributedFunction = contributedFunctionsInSupertypes[TO_STRING] + val toStringContributedFunction = contributedSyntheticFunctions[TO_STRING] if (toStringContributedFunction != null) { result.add(toStringContributedFunction) val toStringFunction = createSyntheticIrFunction( TO_STRING, + toStringContributedFunction, components.irBuiltIns.stringType, - isExpect = klass.isExpect ) irDataClassMembersGenerator.generateToStringMethod(toStringFunction, properties) irClass.declarations.add(toStringFunction) @@ -270,44 +230,12 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrCompon private fun createSyntheticIrFunction( name: Name, + syntheticCounterpart: FirSimpleFunction, returnType: IrType, - isExpect: Boolean, otherParameterNeeded: Boolean = false, isOperator: Boolean = false, ): IrFunction { - val functionSymbol = FirNamedFunctionSymbol(CallableId(lookupTag.classId, name)) - val firFunction = buildSimpleFunction { - origin = FirDeclarationOrigin.Synthetic - this.name = name - this.symbol = functionSymbol - this.status = FirDeclarationStatusImpl(Visibilities.Public, Modality.FINAL).also { it.isExpect = isExpect } - moduleData = components.session.moduleData - this.returnTypeRef = when (returnType) { - components.irBuiltIns.booleanType -> FirImplicitBooleanTypeRef(null) - components.irBuiltIns.intType -> FirImplicitIntTypeRef(null) - components.irBuiltIns.stringType -> FirImplicitStringTypeRef(null) - else -> error("Unexpected synthetic data class function return type: $returnType") - } - if (otherParameterNeeded) { - this.valueParameters.add( - buildValueParameter { - this.name = Name.identifier("other") - origin = FirDeclarationOrigin.Synthetic - moduleData = components.session.moduleData - this.returnTypeRef = FirImplicitNullableAnyTypeRef(null) - this.symbol = FirValueParameterSymbol(this.name) - containingFunctionSymbol = functionSymbol - isCrossinline = false - isNoinline = false - isVararg = false - } - ) - } - dispatchReceiverType = lookupTag.constructType( - (1..irClass.typeParameters.size).map { ConeStarProjection }.toTypedArray(), isNullable = false - ) - } - val signature = if (lookupTag.classId.isLocal) null else components.signatureComposer.composeSignature(firFunction) + val signature = if (lookupTag.classId.isLocal) null else components.signatureComposer.composeSignature(syntheticCounterpart) return components.declarationStorage.declareIrSimpleFunction(signature) { symbol -> components.irFactory.createFunction( UNDEFINED_OFFSET, UNDEFINED_OFFSET, origin, symbol, name, DescriptorVisibilities.PUBLIC, Modality.OPEN, returnType, @@ -316,13 +244,11 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrCompon ).apply { if (otherParameterNeeded) { val irValueParameter = createSyntheticIrParameter( - this, firFunction.valueParameters.first().name, components.irBuiltIns.anyNType + this, syntheticCounterpart.valueParameters.first().name, components.irBuiltIns.anyNType ) this.valueParameters = listOf(irValueParameter) } - metadata = FirMetadataSource.Function( - firFunction - ) + metadata = FirMetadataSource.Function(syntheticCounterpart) } }.apply { parent = irClass diff --git a/compiler/testData/codegen/box/inlineClasses/callableReferences/equalsHashCodeToString.kt b/compiler/testData/codegen/box/inlineClasses/callableReferences/equalsHashCodeToString.kt index df9ff4657ff..f9db79a4bd7 100644 --- a/compiler/testData/codegen/box/inlineClasses/callableReferences/equalsHashCodeToString.kt +++ b/compiler/testData/codegen/box/inlineClasses/callableReferences/equalsHashCodeToString.kt @@ -1,7 +1,6 @@ // IGNORE_BACKEND: WASM // WASM_MUTE_REASON: IGNORED_IN_JS // IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE -// IGNORE_BACKEND_K2: JVM_IR // WITH_REFLECT // WITH_STDLIB // WORKS_WHEN_VALUE_CLASS diff --git a/compiler/testData/codegen/box/inlineClasses/callableReferences/equalsHashCodeToStringGeneric.kt b/compiler/testData/codegen/box/inlineClasses/callableReferences/equalsHashCodeToStringGeneric.kt index 6e9cf65e976..f36317e6d66 100644 --- a/compiler/testData/codegen/box/inlineClasses/callableReferences/equalsHashCodeToStringGeneric.kt +++ b/compiler/testData/codegen/box/inlineClasses/callableReferences/equalsHashCodeToStringGeneric.kt @@ -1,7 +1,6 @@ // IGNORE_BACKEND: WASM // WASM_MUTE_REASON: IGNORED_IN_JS // IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE -// IGNORE_BACKEND_K2: JVM_IR // WITH_REFLECT // WITH_STDLIB // WORKS_WHEN_VALUE_CLASS