[IR] Split implementation of DataClassMembersGenerator to IR based and descriptor based

This commit is contained in:
Dmitriy Novozhilov
2023-06-22 11:25:53 +03:00
committed by Space Team
parent 27f4b53570
commit 6bb7fc05df
19 changed files with 214 additions and 193 deletions
@@ -6,9 +6,6 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.builders.IrGeneratorContextBase
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
@@ -54,29 +51,21 @@ open class LoweringDataClassMemberGenerator(
origin: IrDeclarationOrigin,
forbidDirectFieldAccess: Boolean = false
) :
DataClassMembersGenerator(
IrBasedDataClassMembersGenerator(
IrGeneratorContextBase(backendContext.irBuiltIns),
backendContext.ir.symbols.externalSymbolTable,
irClass,
irClass.kotlinFqName,
origin,
forbidDirectFieldAccess
forbidDirectFieldAccess,
) {
override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction {
error("Descriptor API shouldn't be used in lowerings")
}
override fun generateSyntheticFunctionParameterDeclarations(irFunction: IrFunction) {
// no-op — irFunction from lowering should already have necessary parameters
}
override fun getProperty(parameter: ValueParameterDescriptor?, irValueParameter: IrValueParameter?): IrProperty? {
error("Descriptor API shouldn't be used in lowerings")
}
override fun transform(typeParameterDescriptor: TypeParameterDescriptor): IrType {
error("Descriptor API shouldn't be used in lowerings")
override fun getProperty(irValueParameter: IrValueParameter?): IrProperty {
error("This API shouldn't be used in lowerings")
}
override fun getHashCodeFunctionInfo(type: IrType): HashCodeFunctionInfo {
@@ -20,13 +20,19 @@ import org.jetbrains.kotlin.backend.common.DataClassMethodGenerator
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.builders.IrGeneratorContext
import org.jetbrains.kotlin.ir.builders.irExprBody
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.DataClassMembersGenerator
import org.jetbrains.kotlin.ir.util.declareSimpleFunctionWithOverrides
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtParameter
@@ -74,8 +80,10 @@ internal class DataClassMembersGenerator(
val origin: IrDeclarationOrigin
) : DataClassMethodGenerator(ktClassOrObject, declarationGenerator.context.bindingContext) {
private val irDataClassMembersGenerator = object : DataClassMembersGenerator(
context, context.symbolTable, irClass, ktClassOrObject.fqName, origin, generateBodies = generateBodies
private val irDataClassMembersGenerator = object : DescriptorBasedDataClassMembersGenerator(
context, context.symbolTable, irClass, ktClassOrObject.fqName, origin,
forbidDirectFieldAccess = false,
generateBodies = generateBodies
) {
override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction =
declareSimpleFunction(startOffset, endOffset, origin, functionDescriptor)
@@ -84,14 +92,10 @@ internal class DataClassMembersGenerator(
FunctionGenerator(declarationGenerator).generateSyntheticFunctionParameterDeclarations(irFunction)
}
override fun getProperty(parameter: ValueParameterDescriptor?, irValueParameter: IrValueParameter?): IrProperty? =
parameter?.let {
val property = getOrFail(BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameter)
return getIrProperty(property)
}
override fun transform(typeParameterDescriptor: TypeParameterDescriptor): IrType =
typeParameterDescriptor.defaultType.toIrType()
override fun getProperty(parameter: ValueParameterDescriptor): IrProperty {
val property = getOrFail(BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameter)
return getIrProperty(property)
}
private fun MemberScope.findHashCodeFunctionOrNull() =
getContributedFunctions(Name.identifier("hashCode"), NoLookupLocation.FROM_BACKEND)
@@ -139,12 +143,16 @@ internal class DataClassMembersGenerator(
}
return Psi2IrHashCodeFunctionInfo(symbol, substituted ?: symbol.descriptor)
}
override fun IrConstructorSymbol.typesOfTypeParameters(): List<IrType> {
return descriptor.typeParameters.map { it.defaultType.toIrType() }
}
}
override fun generateComponentFunction(function: FunctionDescriptor, parameter: ValueParameterDescriptor) {
if (!irClass.isData) return
val irProperty = irDataClassMembersGenerator.getProperty(parameter, null) ?: return
val irProperty = irDataClassMembersGenerator.getProperty(parameter)
irDataClassMembersGenerator.generateComponentFunction(function, irProperty)
}
@@ -168,3 +176,88 @@ internal class DataClassMembersGenerator(
irDataClassMembersGenerator.generateToStringMethod(function, properties)
}
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
private abstract class DescriptorBasedDataClassMembersGenerator(
context: IrGeneratorContext,
symbolTable: ReferenceSymbolTable,
irClass: IrClass,
fqName: FqName?,
origin: IrDeclarationOrigin,
forbidDirectFieldAccess: Boolean,
val generateBodies: Boolean
) : DataClassMembersGenerator(context, symbolTable, irClass, fqName, origin, forbidDirectFieldAccess) {
private val irPropertiesByDescriptor: Map<PropertyDescriptor, IrProperty> =
irClass.properties.associateBy { it.descriptor }
fun generateEqualsMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
buildMember(function) {
generateEqualsMethodBody(properties.map { getIrProperty(it) })
}
}
fun generateComponentFunction(function: FunctionDescriptor, irProperty: IrProperty) {
buildMember(function) {
generateComponentFunction(irProperty)
}
}
fun generateCopyFunction(function: FunctionDescriptor, constructorSymbol: IrConstructorSymbol) {
buildMember(function) {
if (generateBodies) {
function.valueParameters.forEach { parameter ->
putDefault(parameter, irGetProperty(irThis(), getProperty(parameter)))
}
generateCopyFunction(constructorSymbol)
}
}
}
fun generateHashCodeMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
buildMember(function) {
generateHashCodeMethodBody(
properties.map { getIrProperty(it) },
if (irClass.kind == ClassKind.OBJECT && irClass.isData) fqName.hashCode() else 0
)
}
}
fun generateToStringMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
buildMember(function) {
generateToStringMethodBody(properties.map { getIrProperty(it) })
}
}
fun getIrProperty(property: PropertyDescriptor): IrProperty {
return irPropertiesByDescriptor[property]
?: error("Class: ${irClass.descriptor}: unexpected property descriptor: $property")
}
abstract fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction
abstract fun getProperty(parameter: ValueParameterDescriptor): IrProperty
// Build a member from a descriptor (psi2ir) as well as its body.
private inline fun buildMember(
function: FunctionDescriptor,
startOffset: Int = SYNTHETIC_OFFSET,
endOffset: Int = SYNTHETIC_OFFSET,
body: MemberFunctionBuilder.(IrFunction) -> Unit
) {
MemberFunctionBuilder(startOffset, endOffset, declareSimpleFunction(startOffset, endOffset, function)).addToClass { irFunction ->
irFunction.buildWithScope {
irFunction.parent = irClass
generateSyntheticFunctionParameterDeclarations(irFunction)
body(irFunction)
}
}
}
private fun MemberFunctionBuilder.putDefault(parameter: ValueParameterDescriptor, value: IrExpression) {
irFunction.putDefault(parameter, irExprBody(value))
}
override fun IrSimpleFunctionSymbol.hasDispatchReceiver(): Boolean {
return descriptor.dispatchReceiverParameter != null
}
}
@@ -5,19 +5,22 @@
package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.addArgument
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.putArgument
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -29,7 +32,6 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
*
* Generating synthetic members of inline class can use this as well, in particular, members from Any: equals, hashCode, and toString.
*/
@OptIn(ObsoleteDescriptorBasedAPI::class)
abstract class DataClassMembersGenerator(
val context: IrGeneratorContext,
val symbolTable: ReferenceSymbolTable,
@@ -37,11 +39,7 @@ abstract class DataClassMembersGenerator(
val fqName: FqName?,
val origin: IrDeclarationOrigin,
val forbidDirectFieldAccess: Boolean = false,
val generateBodies: Boolean = false
) {
private val irPropertiesByDescriptor: Map<PropertyDescriptor, IrProperty> =
irClass.properties.associateBy { it.descriptor }
inline fun <T : IrDeclaration> T.buildWithScope(builder: (T) -> Unit): T =
also { irDeclaration ->
symbolTable.withReferenceScope(irDeclaration) {
@@ -52,7 +50,7 @@ abstract class DataClassMembersGenerator(
protected val IrProperty.type
get() = this.backingField?.type ?: this.getter?.returnType ?: error("Can't find type of ${this.render()}")
private inner class MemberFunctionBuilder(
protected inner class MemberFunctionBuilder(
startOffset: Int = SYNTHETIC_OFFSET,
endOffset: Int = SYNTHETIC_OFFSET,
val irFunction: IrFunction
@@ -79,7 +77,7 @@ abstract class DataClassMembersGenerator(
)
}
fun irOther(): IrExpression {
private fun irOther(): IrExpression {
val irFirstParameter = irFunction.valueParameters[0]
return IrGetValueImpl(
startOffset, endOffset,
@@ -102,10 +100,6 @@ abstract class DataClassMembersGenerator(
}
}
fun putDefault(parameter: ValueParameterDescriptor, value: IrExpression) {
irFunction.putDefault(parameter, irExprBody(value))
}
fun generateComponentFunction(irProperty: IrProperty) {
+irReturn(irGetProperty(irThis(), irProperty))
}
@@ -117,8 +111,8 @@ abstract class DataClassMembersGenerator(
irClass.defaultType,
constructedClass = irClass
).apply {
for ((i, typeParameter) in constructorSymbol.descriptor.typeParameters.withIndex()) {
putTypeArgument(i, transform(typeParameter))
for ((i, typeParameterType) in constructorSymbol.typesOfTypeParameters().withIndex()) {
putTypeArgument(i, typeParameterType)
}
for ((i, valueParameter) in irFunction.valueParameters.withIndex()) {
putValueArgument(i, irGet(valueParameter.type, valueParameter.symbol))
@@ -246,7 +240,7 @@ abstract class DataClassMembersGenerator(
protected fun IrBuilderWithScope.getHashCodeOf(type: IrType, irValue: IrExpression): IrExpression {
val hashCodeFunctionInfo = getHashCodeFunctionInfo(type)
val hashCodeFunctionSymbol = hashCodeFunctionInfo.symbol
val hasDispatchReceiver = hashCodeFunctionSymbol.descriptor.dispatchReceiverParameter != null
val hasDispatchReceiver = hashCodeFunctionSymbol.hasDispatchReceiver()
return irCall(
hashCodeFunctionSymbol,
context.irBuiltIns.intType,
@@ -262,35 +256,69 @@ abstract class DataClassMembersGenerator(
}
}
fun getIrProperty(property: PropertyDescriptor): IrProperty =
irPropertiesByDescriptor[property]
?: throw AssertionError("Class: ${irClass.descriptor}: unexpected property descriptor: $property")
val IrClassifierSymbol?.isArrayOrPrimitiveArray: Boolean
get() = isArrayOrPrimitiveArray(context.irBuiltIns)
abstract fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction
abstract fun generateSyntheticFunctionParameterDeclarations(irFunction: IrFunction)
// Build a member from a descriptor (psi2ir) as well as its body.
private inline fun buildMember(
function: FunctionDescriptor,
startOffset: Int = SYNTHETIC_OFFSET,
endOffset: Int = SYNTHETIC_OFFSET,
body: MemberFunctionBuilder.(IrFunction) -> Unit
) {
MemberFunctionBuilder(startOffset, endOffset, declareSimpleFunction(startOffset, endOffset, function)).addToClass { irFunction ->
irFunction.buildWithScope {
irFunction.parent = irClass
generateSyntheticFunctionParameterDeclarations(irFunction)
body(irFunction)
}
interface HashCodeFunctionInfo {
val symbol: IrSimpleFunctionSymbol
fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression<*>)
}
abstract fun getHashCodeFunctionInfo(type: IrType): HashCodeFunctionInfo
open fun IrClass.classNameForToString(): String = irClass.name.asString()
protected abstract fun IrSimpleFunctionSymbol.hasDispatchReceiver(): Boolean
protected abstract fun IrConstructorSymbol.typesOfTypeParameters(): List<IrType>
}
abstract class IrBasedDataClassMembersGenerator(
context: IrGeneratorContext,
symbolTable: ReferenceSymbolTable,
irClass: IrClass,
fqName: FqName?,
origin: IrDeclarationOrigin,
forbidDirectFieldAccess: Boolean,
) : DataClassMembersGenerator(context, symbolTable, irClass, fqName, origin, forbidDirectFieldAccess) {
fun generateComponentFunction(irFunction: IrFunction, irProperty: IrProperty) {
buildMember(irFunction) {
generateComponentFunction(irProperty)
}
}
// Use a prebuilt member (fir2ir) and build a member body for it.
fun generateCopyFunction(irFunction: IrFunction, constructorSymbol: IrConstructorSymbol) {
buildMember(irFunction) {
irFunction.valueParameters.forEach { irValueParameter ->
irValueParameter.defaultValue = irExprBody(irGetProperty(irThis(), getProperty(irValueParameter)))
}
generateCopyFunction(constructorSymbol)
}
}
fun generateEqualsMethod(irFunction: IrFunction, properties: List<IrProperty>) {
buildMember(irFunction) {
generateEqualsMethodBody(properties)
}
}
fun generateHashCodeMethod(irFunction: IrFunction, properties: List<IrProperty>) {
buildMember(irFunction) {
generateHashCodeMethodBody(
properties,
if (irClass.kind == ClassKind.OBJECT && irClass.isData) fqName.hashCode() else 0
)
}
}
fun generateToStringMethod(irFunction: IrFunction, properties: List<IrProperty>) {
buildMember(irFunction) {
generateToStringMethodBody(properties)
}
}
// Use a prebuilt member and build a member body for it.
private inline fun buildMember(
irFunction: IrFunction,
startOffset: Int = SYNTHETIC_OFFSET,
@@ -305,100 +333,14 @@ abstract class DataClassMembersGenerator(
}
}
// Entry for psi2ir
fun generateComponentFunction(function: FunctionDescriptor, irProperty: IrProperty) {
buildMember(function) {
generateComponentFunction(irProperty)
}
abstract fun getProperty(irValueParameter: IrValueParameter?): IrProperty
override fun IrSimpleFunctionSymbol.hasDispatchReceiver(): Boolean {
return owner.dispatchReceiverParameter != null
}
// Entry for fir2ir
fun generateComponentFunction(irFunction: IrFunction, irProperty: IrProperty) {
buildMember(irFunction) {
generateComponentFunction(irProperty)
}
override fun IrConstructorSymbol.typesOfTypeParameters(): List<IrType> {
val allParameters = owner.constructedClass.typeParameters + owner.typeParameters
return allParameters.map { it.defaultType }
}
abstract fun getProperty(parameter: ValueParameterDescriptor?, irValueParameter: IrValueParameter?): IrProperty?
abstract fun transform(typeParameterDescriptor: TypeParameterDescriptor): IrType
// Entry for psi2ir
fun generateCopyFunction(function: FunctionDescriptor, constructorSymbol: IrConstructorSymbol) {
buildMember(function) {
if (generateBodies) {
function.valueParameters.forEach { parameter ->
putDefault(parameter, irGetProperty(irThis(), getProperty(parameter, null)!!))
}
generateCopyFunction(constructorSymbol)
}
}
}
// Entry for fir2ir
fun generateCopyFunction(irFunction: IrFunction, constructorSymbol: IrConstructorSymbol) {
buildMember(irFunction) {
irFunction.valueParameters.forEach { irValueParameter ->
irValueParameter.defaultValue = irExprBody(irGetProperty(irThis(), getProperty(null, irValueParameter)!!))
}
generateCopyFunction(constructorSymbol)
}
}
// Entry for psi2ir
fun generateEqualsMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
buildMember(function) {
generateEqualsMethodBody(properties.map { getIrProperty(it) })
}
}
// Entry for fir2ir
fun generateEqualsMethod(irFunction: IrFunction, properties: List<IrProperty>) {
buildMember(irFunction) {
generateEqualsMethodBody(properties)
}
}
interface HashCodeFunctionInfo {
val symbol: IrSimpleFunctionSymbol
fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression<*>)
}
abstract fun getHashCodeFunctionInfo(type: IrType): HashCodeFunctionInfo
// Entry for psi2ir
fun generateHashCodeMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
buildMember(function) {
generateHashCodeMethodBody(
properties.map { getIrProperty(it) },
if (irClass.kind == ClassKind.OBJECT && irClass.isData) fqName.hashCode() else 0
)
}
}
// Entry for fir2ir
fun generateHashCodeMethod(irFunction: IrFunction, properties: List<IrProperty>) {
buildMember(irFunction) {
generateHashCodeMethodBody(
properties,
if (irClass.kind == ClassKind.OBJECT && irClass.isData) fqName.hashCode() else 0
)
}
}
// Entry for psi2ir
fun generateToStringMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
buildMember(function) {
generateToStringMethodBody(properties.map { getIrProperty(it) })
}
}
// Entry for fir2ir
fun generateToStringMethod(irFunction: IrFunction, properties: List<IrProperty>) {
buildMember(irFunction) {
generateToStringMethodBody(properties)
}
}
open fun IrClass.classNameForToString(): String = irClass.name.asString()
}