IR: more consolidation of synthetic member generation for data class.

This commit is contained in:
Jinseong Jeon
2020-04-15 13:45:54 -07:00
committed by Dmitriy Novozhilov
parent 7f02d57d88
commit 9f1ecadd65
22 changed files with 354 additions and 403 deletions
@@ -120,11 +120,24 @@ internal class ClassMemberGenerator(
irFunction.body = body
}
} else if (irFunction !is IrConstructor) {
if (irFunction.origin == IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER) {
val kind = Fir2IrDeclarationStorage.ENUM_SYNTHETIC_NAMES.getValue(irFunction.name)
irFunction.body = IrSyntheticBodyImpl(startOffset, endOffset, kind)
} else {
irFunction.body = firFunction?.body?.let { visitor.convertToIrBlockBody(it) }
when {
irFunction.origin == IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER -> {
val kind = Fir2IrDeclarationStorage.ENUM_SYNTHETIC_NAMES.getValue(irFunction.name)
irFunction.body = IrSyntheticBodyImpl(startOffset, endOffset, kind)
}
irFunction.parent is IrClass && irFunction.parentAsClass.isData -> {
when {
DataClassMembersGenerator.isComponentN(irFunction) ->
DataClassMembersGenerator(components).generateDataClassComponentBody(irFunction)
DataClassMembersGenerator.isCopy(irFunction) ->
DataClassMembersGenerator(components).generateDataClassCopyBody(irFunction)
else ->
irFunction.body = firFunction?.body?.let { visitor.convertToIrBlockBody(it) }
}
}
else -> {
irFunction.body = firFunction?.body?.let { visitor.convertToIrBlockBody(it) }
}
}
}
if (irFunction !is IrConstructor || !irFunction.isPrimary) {
@@ -5,10 +5,7 @@
package org.jetbrains.kotlin.fir.backend.generators
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.fir.backend.Fir2IrComponents
import org.jetbrains.kotlin.fir.backend.declareThisReceiverParameter
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
@@ -20,8 +17,8 @@ import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor
import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.DataClassMembersGenerator
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.name.Name
class DataClassMembersGenerator(val components: Fir2IrComponents) {
@@ -31,6 +28,15 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) {
fun generateDataClassMembers(irClass: IrClass): List<Name> =
MyDataClassMethodsGenerator(irClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER).generate()
fun generateDataClassComponentBody(irFunction: IrFunction) =
MyDataClassMethodsGenerator(irFunction.parentAsClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
.generateComponentBody(irFunction)
fun generateDataClassCopyBody(irFunction: IrFunction) =
MyDataClassMethodsGenerator(irFunction.parentAsClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
.generateCopyBody(irFunction)
private inner class MyDataClassMethodsGenerator(
val irClass: IrClass,
val origin: IrDeclarationOrigin
@@ -51,6 +57,18 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) {
// TODO
}
override fun getBackingField(parameter: ValueParameterDescriptor?, irValueParameter: IrValueParameter?): IrField? =
irValueParameter?.let {
irClass.properties.single { irProperty ->
irProperty.name == irValueParameter.name && irProperty.backingField?.type == irValueParameter.type
}.backingField
}
override fun transform(typeParameterDescriptor: TypeParameterDescriptor): IrType {
// TODO
return components.irBuiltIns.anyType
}
override fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression, descriptor: CallableDescriptor) {
// TODO
}
@@ -72,34 +90,46 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) {
return emptyList()
}
// TODO: consolidate componentN() and copy(...) too?
// TODO: generate equals, hashCode, and toString only if needed
val equalsFunction = createSyntheticIrFunction(
Name.identifier("equals"),
components.irBuiltIns.booleanType,
components.irBuiltIns.booleanType
).apply {
valueParameters = listOf(
createSyntheticIrParameter(this, Name.identifier("other"), components.irBuiltIns.anyNType)
)
}
irDataClassMembersGenerator.generateEqualsMethod(equalsFunction, properties)
irClass.declarations.add(equalsFunction)
val hashCodeFunction = createSyntheticIrFunction(
Name.identifier("hashCode"),
components.irBuiltIns.intType,
components.irBuiltIns.intType
)
irDataClassMembersGenerator.generateHashCodeMethod(hashCodeFunction, properties)
irClass.declarations.add(hashCodeFunction)
val toStringFunction = createSyntheticIrFunction(
Name.identifier("toString"),
components.irBuiltIns.stringType,
components.irBuiltIns.stringType
)
irDataClassMembersGenerator.generateToStringMethod(toStringFunction, properties)
irClass.declarations.add(toStringFunction)
return listOf(equalsFunction.name, hashCodeFunction.name, toStringFunction.name)
}
fun generateComponentBody(irFunction: IrFunction) {
val index = getComponentIndex(irFunction)!!
val valueParameter = irClass.primaryConstructor!!.valueParameters[index - 1]
val backingField = irDataClassMembersGenerator.getBackingField(null, valueParameter)!!
irDataClassMembersGenerator
.generateComponentFunction(irFunction, backingField, valueParameter.startOffset, valueParameter.endOffset)
}
fun generateCopyBody(irFunction: IrFunction) =
irDataClassMembersGenerator.generateCopyFunction(irFunction, irClass.primaryConstructor!!.symbol)
private fun createSyntheticIrFunction(name: Name, returnType: IrType): IrFunction {
val functionDescriptor = WrappedSimpleFunctionDescriptor()
val thisReceiverDescriptor = WrappedValueParameterDescriptor()
@@ -130,7 +160,7 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) {
}
}
private fun createSyntheticIrParameter(irFunction: IrFunction, name: Name, type: IrType): IrValueParameter {
private fun createSyntheticIrParameter(irFunction: IrFunction, name: Name, type: IrType, index: Int = 0): IrValueParameter {
val descriptor = WrappedValueParameterDescriptor()
return components.symbolTable.declareValueParameter(
UNDEFINED_OFFSET,
@@ -145,7 +175,7 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) {
origin,
symbol,
name,
0,
index,
type,
null,
isCrossinline = false,
@@ -157,4 +187,26 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) {
}
}
}
companion object {
private val copyName = Name.identifier("copy")
fun isCopy(irFunction: IrFunction): Boolean =
irFunction.name == copyName
fun isComponentN(irFunction: IrFunction): Boolean {
if (irFunction.name.isSpecial) {
return false
}
val name = irFunction.name.identifier
if (!name.startsWith("component")) {
return false
}
val n = getComponentIndex(irFunction)
return n != null && n > 0
}
fun getComponentIndex(irFunction: IrFunction): Int? =
irFunction.name.identifier.substring("component".length).toIntOrNull()
}
}
@@ -17,13 +17,11 @@ import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.*
import org.jetbrains.kotlin.fir.expressions.impl.buildSingleExpressionBlock
import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.references.builder.*
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildImplicitTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
@@ -662,6 +660,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
private fun generateComponentAccess(parameterSource: FirSourceElement?, firProperty: FirProperty) =
buildQualifiedAccessExpression {
source = parameterSource
typeRef = firProperty.returnTypeRef
dispatchReceiver = buildThisReceiverExpression {
calleeReference = buildImplicitThisReference {
boundSymbol = classBuilder.symbol
@@ -682,7 +681,6 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
val name = Name.identifier("component$componentIndex")
componentIndex++
val parameterSource = sourceNode?.toFirSourceElement()
val target = FirFunctionTarget(labelName = null, isLambda = false)
val componentFunction = buildSimpleFunction {
source = parameterSource
session = this@DataClassMembersGenerator.session
@@ -692,14 +690,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
status = FirDeclarationStatusImpl(Visibilities.PUBLIC, Modality.FINAL)
symbol = FirNamedFunctionSymbol(CallableId(packageFqName, classFqName, name))
val returnExpression = buildReturnExpression {
source = parameterSource
result = generateComponentAccess(parameterSource, firProperty)
this.target = target
}
body = buildSingleExpressionBlock(returnExpression)
}.also {
target.bind(it)
// Refer to FIR backend ClassMemberGenerator for body generation.
}
classBuilder.addDeclaration(componentFunction)
}
@@ -708,7 +699,6 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
private val copyName = Name.identifier("copy")
private fun generateCopyFunction() {
val target = FirFunctionTarget(labelName = null, isLambda = false)
classBuilder.addDeclaration(
buildSimpleFunction {
source = this@DataClassMembersGenerator.source.toFirSourceElement()
@@ -732,27 +722,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
isVararg = false
}
}
// TODO: Handle generic types.
val initCallExpression = buildFunctionCall {
argumentList = buildArgumentList {
for ((ktParameter, firProperty) in zippedParameters) {
val parameterSource = ktParameter?.toFirSourceElement()
arguments += generateComponentAccess(parameterSource, firProperty)
}
}
calleeReference = buildResolvedNamedReference {
name = primaryConstructor.symbol.callableId.callableName
resolvedSymbol = primaryConstructor.symbol
}
}
val returnExpression = buildReturnExpression {
result = initCallExpression
this.target = target
}
body = buildSingleExpressionBlock(returnExpression)
}.also {
target.bind(it)
// Refer to FIR backend ClassMemberGenerator for body generation.
}
)
}
@@ -25,9 +25,7 @@ FILE: annotated.kt
super<R|kotlin/Any|>()
}
public final fun copy(): R|Two| {
^copy R|/Two.Two|()
}
public final fun copy(): R|Two|
}
public? final? fun bar(two: Two): R|kotlin/Unit| {
@@ -13,21 +13,13 @@ FILE: destructuring.kt
public? final? val third: String = R|<local>/third|
public? get(): String
public final fun component1(): Int {
^component1 this@R|/Some|.R|/Some.first|
}
public final fun component1(): Int
public final fun component2(): Double {
^component2 this@R|/Some|.R|/Some.second|
}
public final fun component2(): Double
public final fun component3(): String {
^component3 this@R|/Some|.R|/Some.third|
}
public final fun component3(): String
public final fun copy(first: Int = this@R|/Some|.R|/Some.first|, second: Double = this@R|/Some|.R|/Some.second|, third: String = this@R|/Some|.R|/Some.third|): R|Some| {
^copy R|/Some.Some|(this@R|/Some|.R|/Some.first|, this@R|/Some|.R|/Some.second|, this@R|/Some|.R|/Some.third|)
}
public final fun copy(first: Int = this@R|/Some|.R|/Some.first|, second: Double = this@R|/Some|.R|/Some.second|, third: String = this@R|/Some|.R|/Some.third|): R|Some|
}
public? final? fun foo(some: Some): R|kotlin/Unit| {
@@ -48,17 +48,11 @@ FILE: for.kt
public? final? val y: Int = R|<local>/y|
public? get(): Int
public final fun component1(): Int {
^component1 this@R|/Some|.R|/Some.x|
}
public final fun component1(): Int
public final fun component2(): Int {
^component2 this@R|/Some|.R|/Some.y|
}
public final fun component2(): Int
public final fun copy(x: Int = this@R|/Some|.R|/Some.x|, y: Int = this@R|/Some|.R|/Some.y|): R|Some| {
^copy R|/Some.Some|(this@R|/Some|.R|/Some.x|, this@R|/Some|.R|/Some.y|)
}
public final fun copy(x: Int = this@R|/Some|.R|/Some.x|, y: Int = this@R|/Some|.R|/Some.y|): R|Some|
}
public? final? fun baz(set: Set<Some>): R|kotlin/Unit| {
@@ -10,17 +10,11 @@ FILE: lambda.kt
public? final? val y: Int = R|<local>/y|
public? get(): Int
public final fun component1(): Int {
^component1 this@R|/Tuple|.R|/Tuple.x|
}
public final fun component1(): Int
public final fun component2(): Int {
^component2 this@R|/Tuple|.R|/Tuple.y|
}
public final fun component2(): Int
public final fun copy(x: Int = this@R|/Tuple|.R|/Tuple.x|, y: Int = this@R|/Tuple|.R|/Tuple.y|): R|Tuple| {
^copy R|/Tuple.Tuple|(this@R|/Tuple|.R|/Tuple.x|, this@R|/Tuple|.R|/Tuple.y|)
}
public final fun copy(x: Int = this@R|/Tuple|.R|/Tuple.x|, y: Int = this@R|/Tuple|.R|/Tuple.y|): R|Tuple|
}
public? final? inline fun use(f: ( (Tuple) -> Int )): <implicit> {