FIR2IR: fix binding of data class generated members
Related to KT-54844 #KT-54952 Fixed #KT-54887 Fixed
This commit is contained in:
committed by
Space Team
parent
5304afb097
commit
f48142a6e6
+25
-99
@@ -10,27 +10,11 @@ import org.jetbrains.kotlin.descriptors.*
|
|||||||
import org.jetbrains.kotlin.fir.backend.Fir2IrComponents
|
import org.jetbrains.kotlin.fir.backend.Fir2IrComponents
|
||||||
import org.jetbrains.kotlin.fir.backend.FirMetadataSource
|
import org.jetbrains.kotlin.fir.backend.FirMetadataSource
|
||||||
import org.jetbrains.kotlin.fir.backend.declareThisReceiverParameter
|
import org.jetbrains.kotlin.fir.backend.declareThisReceiverParameter
|
||||||
import org.jetbrains.kotlin.fir.backend.toIrType
|
import org.jetbrains.kotlin.fir.containingClassLookupTag
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
import org.jetbrains.kotlin.fir.render
|
||||||
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.scopes.unsubstitutedScope
|
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
|
||||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
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.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.builders.IrGeneratorContextBase
|
import org.jetbrains.kotlin.ir.builders.IrGeneratorContextBase
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
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.types.classifierOrNull
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.util.DataClassMembersGenerator
|
import org.jetbrains.kotlin.ir.util.DataClassMembersGenerator
|
||||||
import org.jetbrains.kotlin.name.CallableId
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.DataClassResolver
|
import org.jetbrains.kotlin.resolve.DataClassResolver
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions.EQUALS
|
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 {
|
class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrComponents by components {
|
||||||
|
|
||||||
fun generateSingleFieldValueClassMembers(klass: FirClass, irClass: IrClass): List<FirDeclaration> =
|
fun generateSingleFieldValueClassMembers(klass: FirRegularClass, irClass: IrClass): List<FirDeclaration> =
|
||||||
MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER)
|
MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER)
|
||||||
.generate(klass)
|
.generate(klass)
|
||||||
|
|
||||||
fun generateMultiFieldValueClassMembers(klass: FirClass, irClass: IrClass): List<FirDeclaration> =
|
fun generateMultiFieldValueClassMembers(klass: FirRegularClass, irClass: IrClass): List<FirDeclaration> =
|
||||||
MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER)
|
MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER)
|
||||||
.generate(klass)
|
.generate(klass)
|
||||||
|
|
||||||
fun generateDataClassMembers(klass: FirClass, irClass: IrClass): List<FirDeclaration> =
|
fun generateDataClassMembers(klass: FirRegularClass, irClass: IrClass): List<FirDeclaration> =
|
||||||
MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER).generate(klass)
|
MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER).generate(klass)
|
||||||
|
|
||||||
fun generateDataClassComponentBody(irFunction: IrFunction, lookupTag: ConeClassLikeLookupTag) =
|
fun generateDataClassComponentBody(irFunction: IrFunction, lookupTag: ConeClassLikeLookupTag) =
|
||||||
@@ -163,64 +146,41 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrCompon
|
|||||||
UNDEFINED_OFFSET
|
UNDEFINED_OFFSET
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun generate(klass: FirRegularClass): List<FirDeclaration> {
|
||||||
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<FirDeclaration> {
|
|
||||||
val propertyParametersCount = irClass.primaryConstructor?.explicitParameters?.size ?: 0
|
val propertyParametersCount = irClass.primaryConstructor?.explicitParameters?.size ?: 0
|
||||||
val properties = irClass.properties.filter { it.backingField != null }.take(propertyParametersCount).toList()
|
val properties = irClass.properties.filter { it.backingField != null }.take(propertyParametersCount).toList()
|
||||||
|
|
||||||
val result = mutableListOf<FirDeclaration>()
|
val result = mutableListOf<FirDeclaration>()
|
||||||
|
|
||||||
val contributedFunctionsInThisType = klass.declarations.mapNotNull {
|
|
||||||
if (it is FirSimpleFunction && it.matchesDataClassSyntheticMemberSignatures) {
|
|
||||||
it.name
|
|
||||||
} else
|
|
||||||
null
|
|
||||||
}
|
|
||||||
val scope = klass.unsubstitutedScope(
|
val scope = klass.unsubstitutedScope(
|
||||||
components.session,
|
components.session,
|
||||||
components.scopeSession,
|
components.scopeSession,
|
||||||
withForcedTypeCalculator = true,
|
withForcedTypeCalculator = true,
|
||||||
memberRequiredPhase = null,
|
memberRequiredPhase = null,
|
||||||
)
|
)
|
||||||
val contributedFunctionsInSupertypes =
|
val contributedSyntheticFunctions =
|
||||||
buildMap<Name, FirSimpleFunction> {
|
buildMap<Name, FirSimpleFunction> {
|
||||||
for (name in listOf(EQUALS, HASHCODE_NAME, TO_STRING)) {
|
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) {
|
scope.processFunctionsByName(name) {
|
||||||
val declaration = it.fir
|
// We won't synthesize a function if there is a user-contributed (non-synthetic) one.
|
||||||
if (declaration.matchesDataClassSyntheticMemberSignatures && declaration.modality != Modality.FINAL) {
|
if (it.origin != FirDeclarationOrigin.Synthetic) return@processFunctionsByName
|
||||||
putIfAbsent(declaration.name, declaration)
|
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) {
|
if (equalsContributedFunction != null) {
|
||||||
result.add(equalsContributedFunction)
|
result.add(equalsContributedFunction)
|
||||||
val equalsFunction = createSyntheticIrFunction(
|
val equalsFunction = createSyntheticIrFunction(
|
||||||
EQUALS,
|
EQUALS,
|
||||||
|
equalsContributedFunction,
|
||||||
components.irBuiltIns.booleanType,
|
components.irBuiltIns.booleanType,
|
||||||
isExpect = klass.isExpect,
|
|
||||||
otherParameterNeeded = true,
|
otherParameterNeeded = true,
|
||||||
isOperator = true
|
isOperator = true
|
||||||
)
|
)
|
||||||
@@ -228,25 +188,25 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrCompon
|
|||||||
irClass.declarations.add(equalsFunction)
|
irClass.declarations.add(equalsFunction)
|
||||||
}
|
}
|
||||||
|
|
||||||
val hashcodeNameContributedFunction = contributedFunctionsInSupertypes[HASHCODE_NAME]
|
val hashcodeNameContributedFunction = contributedSyntheticFunctions[HASHCODE_NAME]
|
||||||
if (hashcodeNameContributedFunction != null) {
|
if (hashcodeNameContributedFunction != null) {
|
||||||
result.add(hashcodeNameContributedFunction)
|
result.add(hashcodeNameContributedFunction)
|
||||||
val hashCodeFunction = createSyntheticIrFunction(
|
val hashCodeFunction = createSyntheticIrFunction(
|
||||||
HASHCODE_NAME,
|
HASHCODE_NAME,
|
||||||
|
hashcodeNameContributedFunction,
|
||||||
components.irBuiltIns.intType,
|
components.irBuiltIns.intType,
|
||||||
isExpect = klass.isExpect
|
|
||||||
)
|
)
|
||||||
irDataClassMembersGenerator.generateHashCodeMethod(hashCodeFunction, properties)
|
irDataClassMembersGenerator.generateHashCodeMethod(hashCodeFunction, properties)
|
||||||
irClass.declarations.add(hashCodeFunction)
|
irClass.declarations.add(hashCodeFunction)
|
||||||
}
|
}
|
||||||
|
|
||||||
val toStringContributedFunction = contributedFunctionsInSupertypes[TO_STRING]
|
val toStringContributedFunction = contributedSyntheticFunctions[TO_STRING]
|
||||||
if (toStringContributedFunction != null) {
|
if (toStringContributedFunction != null) {
|
||||||
result.add(toStringContributedFunction)
|
result.add(toStringContributedFunction)
|
||||||
val toStringFunction = createSyntheticIrFunction(
|
val toStringFunction = createSyntheticIrFunction(
|
||||||
TO_STRING,
|
TO_STRING,
|
||||||
|
toStringContributedFunction,
|
||||||
components.irBuiltIns.stringType,
|
components.irBuiltIns.stringType,
|
||||||
isExpect = klass.isExpect
|
|
||||||
)
|
)
|
||||||
irDataClassMembersGenerator.generateToStringMethod(toStringFunction, properties)
|
irDataClassMembersGenerator.generateToStringMethod(toStringFunction, properties)
|
||||||
irClass.declarations.add(toStringFunction)
|
irClass.declarations.add(toStringFunction)
|
||||||
@@ -270,44 +230,12 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrCompon
|
|||||||
|
|
||||||
private fun createSyntheticIrFunction(
|
private fun createSyntheticIrFunction(
|
||||||
name: Name,
|
name: Name,
|
||||||
|
syntheticCounterpart: FirSimpleFunction,
|
||||||
returnType: IrType,
|
returnType: IrType,
|
||||||
isExpect: Boolean,
|
|
||||||
otherParameterNeeded: Boolean = false,
|
otherParameterNeeded: Boolean = false,
|
||||||
isOperator: Boolean = false,
|
isOperator: Boolean = false,
|
||||||
): IrFunction {
|
): IrFunction {
|
||||||
val functionSymbol = FirNamedFunctionSymbol(CallableId(lookupTag.classId, name))
|
val signature = if (lookupTag.classId.isLocal) null else components.signatureComposer.composeSignature(syntheticCounterpart)
|
||||||
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)
|
|
||||||
return components.declarationStorage.declareIrSimpleFunction(signature) { symbol ->
|
return components.declarationStorage.declareIrSimpleFunction(signature) { symbol ->
|
||||||
components.irFactory.createFunction(
|
components.irFactory.createFunction(
|
||||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, origin, symbol, name, DescriptorVisibilities.PUBLIC, Modality.OPEN, returnType,
|
UNDEFINED_OFFSET, UNDEFINED_OFFSET, origin, symbol, name, DescriptorVisibilities.PUBLIC, Modality.OPEN, returnType,
|
||||||
@@ -316,13 +244,11 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrCompon
|
|||||||
).apply {
|
).apply {
|
||||||
if (otherParameterNeeded) {
|
if (otherParameterNeeded) {
|
||||||
val irValueParameter = createSyntheticIrParameter(
|
val irValueParameter = createSyntheticIrParameter(
|
||||||
this, firFunction.valueParameters.first().name, components.irBuiltIns.anyNType
|
this, syntheticCounterpart.valueParameters.first().name, components.irBuiltIns.anyNType
|
||||||
)
|
)
|
||||||
this.valueParameters = listOf(irValueParameter)
|
this.valueParameters = listOf(irValueParameter)
|
||||||
}
|
}
|
||||||
metadata = FirMetadataSource.Function(
|
metadata = FirMetadataSource.Function(syntheticCounterpart)
|
||||||
firFunction
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}.apply {
|
}.apply {
|
||||||
parent = irClass
|
parent = irClass
|
||||||
|
|||||||
-1
@@ -1,7 +1,6 @@
|
|||||||
// IGNORE_BACKEND: WASM
|
// IGNORE_BACKEND: WASM
|
||||||
// WASM_MUTE_REASON: IGNORED_IN_JS
|
// WASM_MUTE_REASON: IGNORED_IN_JS
|
||||||
// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE
|
// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE
|
||||||
// IGNORE_BACKEND_K2: JVM_IR
|
|
||||||
// WITH_REFLECT
|
// WITH_REFLECT
|
||||||
// WITH_STDLIB
|
// WITH_STDLIB
|
||||||
// WORKS_WHEN_VALUE_CLASS
|
// WORKS_WHEN_VALUE_CLASS
|
||||||
|
|||||||
Vendored
-1
@@ -1,7 +1,6 @@
|
|||||||
// IGNORE_BACKEND: WASM
|
// IGNORE_BACKEND: WASM
|
||||||
// WASM_MUTE_REASON: IGNORED_IN_JS
|
// WASM_MUTE_REASON: IGNORED_IN_JS
|
||||||
// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE
|
// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE
|
||||||
// IGNORE_BACKEND_K2: JVM_IR
|
|
||||||
// WITH_REFLECT
|
// WITH_REFLECT
|
||||||
// WITH_STDLIB
|
// WITH_STDLIB
|
||||||
// WORKS_WHEN_VALUE_CLASS
|
// WORKS_WHEN_VALUE_CLASS
|
||||||
|
|||||||
Reference in New Issue
Block a user