[IR] Final preparation to autogenerate IR declaration implementations

IR expressions are left out for now, because unlike declarations,
which are mostly created via IrFactory, expressions' constructors are
widely used, and it's hard to replicate the exact signatures of those
constructors with the tree generator.

Therefore, some other approach is expected to be taken when generating
them in the future.

^KT-65773 In Progress
This commit is contained in:
Wojciech Litewka
2024-02-21 16:40:11 +01:00
committed by Space Team
parent 451c51c849
commit 6cdddaacb0
13 changed files with 259 additions and 57 deletions
@@ -629,7 +629,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
override fun configureAllImplementations(model: Model) { override fun configureAllImplementations(model: Model) {
configureFieldInAllImplementations( configureFieldInAllImplementations(
field = "controlFlowGraphReference", fieldName = "controlFlowGraphReference",
implementationPredicate = { it.typeName != "FirAnonymousFunctionImpl" } implementationPredicate = { it.typeName != "FirAnonymousFunctionImpl" }
) { ) {
defaultNull(it) defaultNull(it)
@@ -661,7 +661,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
"FirInaccessibleReceiverExpressionImpl" "FirInaccessibleReceiverExpressionImpl"
) )
configureFieldInAllImplementations( configureFieldInAllImplementations(
field = "typeRef", fieldName = "typeRef",
implementationPredicate = { it.typeName !in implementationWithConfigurableTypeRef }, implementationPredicate = { it.typeName !in implementationWithConfigurableTypeRef },
fieldPredicate = { it.defaultValueInImplementation == null } fieldPredicate = { it.defaultValueInImplementation == null }
) { ) {
@@ -670,7 +670,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
} }
configureFieldInAllImplementations( configureFieldInAllImplementations(
field = "lValueTypeRef", fieldName = "lValueTypeRef",
implementationPredicate = { it.typeName in "FirVariableAssignmentImpl" }, implementationPredicate = { it.typeName in "FirVariableAssignmentImpl" },
fieldPredicate = { it.defaultValueInImplementation == null } fieldPredicate = { it.defaultValueInImplementation == null }
) { ) {
@@ -27,6 +27,9 @@ private class ImplementationFieldPrinter(printer: SmartPrinter) : AbstractFieldP
override fun forceMutable(field: FieldWithDefault): Boolean = field.isMutable && field.isMutableOrEmptyIfList() override fun forceMutable(field: FieldWithDefault): Boolean = field.isMutable && field.isMutableOrEmptyIfList()
override fun actualTypeOfField(field: FieldWithDefault) = field.getMutableType() override fun actualTypeOfField(field: FieldWithDefault) = field.getMutableType()
override val wrapOptInAnnotations
get() = true
} }
internal class ImplementationPrinter( internal class ImplementationPrinter(
@@ -5,16 +5,139 @@
package org.jetbrains.kotlin.ir.generator package org.jetbrains.kotlin.ir.generator
import org.jetbrains.kotlin.generators.tree.StandardTypes
import org.jetbrains.kotlin.ir.generator.config.AbstractIrTreeImplementationConfigurator import org.jetbrains.kotlin.ir.generator.config.AbstractIrTreeImplementationConfigurator
import org.jetbrains.kotlin.ir.generator.model.Element import org.jetbrains.kotlin.ir.generator.model.Element
import org.jetbrains.kotlin.ir.generator.model.ListField
object ImplementationConfigurator : AbstractIrTreeImplementationConfigurator() { object ImplementationConfigurator : AbstractIrTreeImplementationConfigurator() {
override fun configure(model: Model): Unit = with(IrTree) { override fun configure(model: Model): Unit = with(IrTree) {
impl(simpleFunction) impl(anonymousInitializer) {
impl(property) implementation.doPrint = false
}
impl(simpleFunction) {
implementation.doPrint = false
}
impl(functionWithLateBinding) {
implementation.doPrint = false
}
impl(constructor) {
implementation.doPrint = false
}
impl(field) {
implementation.doPrint = false
}
impl(property) {
implementation.doPrint = false
}
impl(propertyWithLateBinding) {
implementation.doPrint = false
}
impl(localDelegatedProperty) {
implementation.doPrint = false
}
impl(typeParameter) {
implementation.doPrint = false
}
impl(valueParameter) {
implementation.doPrint = false
}
impl(variable) {
implementation.doPrint = false
}
impl(`class`) {
implementation.doPrint = false
}
impl(enumEntry) {
implementation.doPrint = false
}
impl(script) {
implementation.doPrint = false
}
impl(moduleFragment) {
implementation.doPrint = false
}
impl(errorDeclaration) {
implementation.doPrint = false
}
impl(externalPackageFragment) {
implementation.doPrint = false
}
impl(file) {
implementation.doPrint = false
}
impl(typeAlias) {
implementation.doPrint = false
}
} }
override fun configureAllImplementations(model: Model) { override fun configureAllImplementations(model: Model) {
// Use configureFieldInAllImplementations to customize certain fields in all implementation classes configureFieldInAllImplementations("parent") {
isLateinit("parent")
isMutable("parent")
}
configureFieldInAllImplementations("attributeOwnerId") {
default(it, "this")
}
configureFieldInAllImplementations("originalBeforeInline") {
defaultNull(it)
}
configureFieldInAllImplementations("metadata") {
defaultNull(it)
}
configureFieldInAllImplementations("annotations") {
defaultEmptyList(it)
}
configureFieldInAllImplementations("overriddenSymbols") {
defaultEmptyList(it)
}
configureFieldInAllImplementations("typeParameters") {
defaultEmptyList(it)
}
configureFieldInAllImplementations("statements") {
default(it, "ArrayList(2)")
}
configureFieldInAllImplementations("descriptor", { impl -> impl.allFields.any { it.name == "symbol" } }) {
default(it, "symbol.descriptor", withGetter = true)
}
configureFieldInAllImplementations(
fieldName = null,
fieldPredicate = { it is ListField && it.isChild && it.listType == StandardTypes.mutableList }
) {
default(it, "ArrayList()")
}
// Generation of implementation classes of IrExpression are left out for subsequent MR, as a part of KT-65773.
for (element in model.elements) {
if (element.category == Element.Category.Expression) {
for (implementation in element.implementations) {
implementation.doPrint = false
}
}
}
} }
} }
@@ -35,6 +35,7 @@ fun main(args: Array<String>) {
typeTransformerType to ::TypeTransformerPrinter.bind(model.rootElement), typeTransformerType to ::TypeTransformerPrinter.bind(model.rootElement),
), ),
ImplementationConfigurator, ImplementationConfigurator,
createImplementationPrinter = ::ImplementationPrinter,
enableBaseTransformerTypeDetection = false, enableBaseTransformerTypeDetection = false,
addFiles = { add(printFactory(generationPath, model)) } addFiles = { add(printFactory(generationPath, model)) }
) )
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.generators.tree.ElementRef as GenericElementRef
class Element( class Element(
name: String, name: String,
override val propertyName: String, override val propertyName: String,
category: Category, val category: Category,
) : AbstractElement<Element, Field, Implementation>(name) { ) : AbstractElement<Element, Field, Implementation>(name) {
enum class Category(private val packageDir: String, val defaultVisitorParam: String) { enum class Category(private val packageDir: String, val defaultVisitorParam: String) {
@@ -7,8 +7,19 @@ package org.jetbrains.kotlin.ir.generator.model
import org.jetbrains.kotlin.generators.tree.AbstractImplementation import org.jetbrains.kotlin.generators.tree.AbstractImplementation
import org.jetbrains.kotlin.generators.tree.ImplementationKind import org.jetbrains.kotlin.generators.tree.ImplementationKind
import org.jetbrains.kotlin.generators.tree.ImportCollector
import org.jetbrains.kotlin.utils.SmartPrinter
class Implementation(element: Element, name: String?) : AbstractImplementation<Implementation, Element, Field>(element, name) { class Implementation(element: Element, name: String?) : AbstractImplementation<Implementation, Element, Field>(element, name) {
override val allFields override val allFields: List<Field> = element.allFields.map { it.copy() }
get() = element.allFields
override var kind: ImplementationKind? = ImplementationKind.FinalClass
var generationCallback: (context(ImportCollector) SmartPrinter.() -> Unit)? = null
override var doPrint = true
init {
isPublic = true
}
} }
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.generator.print
import com.intellij.psi.util.PsiExpressionTrimRenderer.render
import org.jetbrains.kotlin.generators.tree.*
import org.jetbrains.kotlin.generators.tree.printer.printBlock
import org.jetbrains.kotlin.ir.generator.IrTree
import org.jetbrains.kotlin.ir.generator.IrTree.parent
import org.jetbrains.kotlin.ir.generator.irImplementationDetailType
import org.jetbrains.kotlin.ir.generator.model.*
import org.jetbrains.kotlin.utils.SmartPrinter
internal class ImplementationPrinter(printer: SmartPrinter) : AbstractImplementationPrinter<Implementation, Element, Field>(printer) {
override fun makeFieldPrinter(printer: SmartPrinter) = object : AbstractFieldPrinter<Field>(printer) {
override fun forceMutable(field: Field) = field.isMutable
}
override val pureAbstractElementType: ClassRef<*>
get() = org.jetbrains.kotlin.ir.generator.elementBaseType
override val implementationOptInAnnotation: ClassRef<*>
get() = irImplementationDetailType
override val separateFieldsWithBlankLine: Boolean
get() = true
context(ImportCollector)
override fun SmartPrinter.printAdditionalMethods(implementation: Implementation) {
implementation.generationCallback?.invoke(this@ImportCollector, this)
if (
implementation.element.traverseParentsUntil { it == IrTree.symbolOwner } &&
!implementation.element.let { it == IrTree.propertyWithLateBinding || it == IrTree.functionWithLateBinding }
) {
val symbolField = implementation["symbol"]
if (symbolField != null) {
println()
print("init")
printBlock {
println("${symbolField.name}.bind(this)")
}
}
}
}
}
@@ -30,6 +30,9 @@ abstract class AbstractFieldPrinter<Field : AbstractField<*>>(
*/ */
protected open fun actualTypeOfField(field: Field): TypeRefWithNullability = field.typeRef protected open fun actualTypeOfField(field: Field): TypeRefWithNullability = field.typeRef
protected open val wrapOptInAnnotations: Boolean
get() = false
context(ImportCollector) context(ImportCollector)
fun printField( fun printField(
field: Field, field: Field,
@@ -51,9 +54,9 @@ abstract class AbstractFieldPrinter<Field : AbstractField<*>>(
isLateinit = (inImplementation || field.isFinal) && field.isLateinit, isLateinit = (inImplementation || field.isFinal) && field.isLateinit,
isVolatile = (inImplementation || field.isFinal) && field.isVolatile, isVolatile = (inImplementation || field.isFinal) && field.isVolatile,
optInAnnotation = field.optInAnnotation, optInAnnotation = field.optInAnnotation,
printOptInWrapped = defaultValue != null, printOptInWrapped = wrapOptInAnnotations && defaultValue != null,
deprecation = field.deprecation, deprecation = field.deprecation,
kDoc = field.kDoc, kDoc = field.kDoc.takeIf { !inImplementation },
initializer = defaultValue.takeUnless { field.withGetter } initializer = defaultValue.takeUnless { field.withGetter }
) )
println() println()
@@ -64,6 +64,10 @@ abstract class AbstractImplementation<Implementation, Element, Field>(
get() = true get() = true
var isPublic = false var isPublic = false
var putImplementationOptInInConstructor = true
var constructorParameterOrderOverride: List<String>? = null
override fun get(fieldName: String): Field? { override fun get(fieldName: String): Field? {
return allFields.firstOrNull { it.name == fieldName } return allFields.firstOrNull { it.name == fieldName }
} }
@@ -92,4 +96,7 @@ abstract class AbstractImplementation<Implementation, Element, Field>(
} }
var builder: LeafBuilder<Field, Element, Implementation>? = null var builder: LeafBuilder<Field, Element, Implementation>? = null
open val doPrint: Boolean
get() = true
} }
@@ -25,11 +25,13 @@ abstract class AbstractImplementationPrinter<Implementation, Element, Implementa
protected abstract val pureAbstractElementType: ClassRef<*> protected abstract val pureAbstractElementType: ClassRef<*>
protected open val separateFieldsWithBlankLine: Boolean
get() = false
protected abstract fun makeFieldPrinter(printer: SmartPrinter): AbstractFieldPrinter<ImplementationField> protected abstract fun makeFieldPrinter(printer: SmartPrinter): AbstractFieldPrinter<ImplementationField>
context(ImportCollector) context(ImportCollector)
protected open fun SmartPrinter.printAdditionalMethods(implementation: Implementation) { protected open fun SmartPrinter.printAdditionalMethods(implementation: Implementation) {}
}
context(ImportCollector) context(ImportCollector)
fun printImplementation(implementation: Implementation) { fun printImplementation(implementation: Implementation) {
@@ -63,19 +65,21 @@ abstract class AbstractImplementationPrinter<Implementation, Element, Implementa
val fieldPrinter = makeFieldPrinter(this) val fieldPrinter = makeFieldPrinter(this)
if (!isInterface && !isAbstract && implementation.fieldsInConstructor.isNotEmpty()) { if (!isInterface && !isAbstract && implementation.fieldsInConstructor.isNotEmpty()) {
if (implementation.isPublic) { if (implementation.isPublic && implementation.putImplementationOptInInConstructor) {
print(" @", implementationOptInAnnotation.render(), " constructor") print(" @", implementationOptInAnnotation.render(), " constructor")
} }
println("(") println("(")
withIndent { withIndent {
implementation.fieldsInConstructor.forEachIndexed { _, field -> implementation.fieldsInConstructor
if (field.isParameter) { .reorderFieldsIfNecessary(implementation.constructorParameterOrderOverride)
print(field.name, ": ", field.typeRef.render()) .forEachIndexed { _, field ->
println(",") if (field.isParameter) {
} else if (!field.isFinal) { print(field.name, ": ", field.typeRef.render())
fieldPrinter.printField(field, inImplementation = true, override = true, inConstructor = true) println(",")
} else if (!field.isFinal) {
fieldPrinter.printField(field, inImplementation = true, override = true, inConstructor = true)
}
} }
}
} }
print(")") print(")")
} }
@@ -86,23 +90,18 @@ abstract class AbstractImplementationPrinter<Implementation, Element, Implementa
} }
print(implementation.allParents.joinToString { "${it.render()}${it.kind.braces()}" }) print(implementation.allParents.joinToString { "${it.render()}${it.kind.braces()}" })
printBlock { printBlock {
if (isInterface || isAbstract) { val fields = if (isInterface || isAbstract) implementation.allFields
implementation.allFields.forEach { else implementation.fieldsInBody
fieldPrinter.printField( fields.forEachIndexed { index, field ->
it, if (index > 0 && separateFieldsWithBlankLine) {
inImplementation = true, println()
override = true,
modality = Modality.ABSTRACT.takeIf { isAbstract }
)
}
} else {
implementation.fieldsInBody.forEach {
fieldPrinter.printField(
it,
inImplementation = true,
override = true
)
} }
fieldPrinter.printField(
field,
inImplementation = true,
override = true,
modality = Modality.ABSTRACT.takeIf { isAbstract }
)
} }
printAdditionalMethods(implementation) printAdditionalMethods(implementation)
@@ -80,14 +80,14 @@ abstract class AbstractImplementationConfigurator<Implementation, Element, Imple
* Allows to batch-apply [config] to certain fields in _all_ the implementations that satisfy the given * Allows to batch-apply [config] to certain fields in _all_ the implementations that satisfy the given
* [implementationPredicate]. * [implementationPredicate].
* *
* @param field The name of the field to configure across all `Impl` classes. * @param fieldName The name of the field to configure across all `Impl` classes, or `null` if [config] should be applied to all fields.
* @param implementationPredicate Only implementations satisfying this predicate will be used in this configuration. * @param implementationPredicate Only implementations satisfying this predicate will be used in this configuration.
* @param fieldPredicate Only fields satisfying this predicate will be configured * @param fieldPredicate Only fields satisfying this predicate will be configured
* @param config The configuration block. Accepts the field name as an argument. * @param config The configuration block. Accepts the field name as an argument.
* See [ImplementationContext]'s documentation for description of its DSL methods. * See [ImplementationContext]'s documentation for description of its DSL methods.
*/ */
protected fun configureFieldInAllImplementations( protected fun configureFieldInAllImplementations(
field: String, fieldName: String?,
implementationPredicate: (Implementation) -> Boolean = { true }, implementationPredicate: (Implementation) -> Boolean = { true },
fieldPredicate: (ImplementationField) -> Boolean = { true }, fieldPredicate: (ImplementationField) -> Boolean = { true },
config: ImplementationContext.(field: String) -> Unit, config: ImplementationContext.(field: String) -> Unit,
@@ -95,9 +95,17 @@ abstract class AbstractImplementationConfigurator<Implementation, Element, Imple
for (element in elementsWithImpl) { for (element in elementsWithImpl) {
for (implementation in element.implementations) { for (implementation in element.implementations) {
if (!implementationPredicate(implementation)) continue if (!implementationPredicate(implementation)) continue
if (!implementation.allFields.any { it.name == field }) continue if (fieldName != null && !implementation.allFields.any { it.name == fieldName }) continue
if (!fieldPredicate(implementation.getField(field))) continue
ImplementationContext(implementation).config(field) val fields = if (fieldName != null) {
listOf(implementation.getField(fieldName))
} else {
implementation.allFields
}
for (field in fields.filter(fieldPredicate)) {
ImplementationContext(implementation).config(field.name)
}
} }
} }
} }
@@ -83,9 +83,9 @@ fun <Element, Implementation, ElementField, ImplementationField> generateTree(
pureAbstractElement: ClassRef<*>, pureAbstractElement: ClassRef<*>,
createElementPrinter: (SmartPrinter) -> AbstractElementPrinter<Element, ElementField>, createElementPrinter: (SmartPrinter) -> AbstractElementPrinter<Element, ElementField>,
createVisitorPrinters: List<Pair<ClassRef<*>, (SmartPrinter, ClassRef<*>) -> AbstractVisitorPrinter<Element, ElementField>>>, createVisitorPrinters: List<Pair<ClassRef<*>, (SmartPrinter, ClassRef<*>) -> AbstractVisitorPrinter<Element, ElementField>>>,
implementationConfigurator: AbstractImplementationConfigurator<Implementation, Element, ImplementationField>? = null, implementationConfigurator: AbstractImplementationConfigurator<Implementation, Element, ImplementationField>,
builderConfigurator: AbstractBuilderConfigurator<Element, Implementation, ImplementationField, ElementField>? = null, builderConfigurator: AbstractBuilderConfigurator<Element, Implementation, ImplementationField, ElementField>? = null,
createImplementationPrinter: ((SmartPrinter) -> AbstractImplementationPrinter<Implementation, Element, ImplementationField>)? = null, createImplementationPrinter: (SmartPrinter) -> AbstractImplementationPrinter<Implementation, Element, ImplementationField>,
createBuilderPrinter: ((SmartPrinter) -> AbstractBuilderPrinter<Element, Implementation, ImplementationField, ElementField>)? = null, createBuilderPrinter: ((SmartPrinter) -> AbstractBuilderPrinter<Element, Implementation, ImplementationField, ElementField>)? = null,
enableBaseTransformerTypeDetection: Boolean = true, enableBaseTransformerTypeDetection: Boolean = true,
addFiles: MutableList<GeneratedFile>.() -> Unit = {}, addFiles: MutableList<GeneratedFile>.() -> Unit = {},
@@ -98,7 +98,7 @@ fun <Element, Implementation, ElementField, ImplementationField> generateTree(
detectBaseTransformerTypes(model) detectBaseTransformerTypes(model)
} }
initializeSubElements(model.elements) initializeSubElements(model.elements)
implementationConfigurator?.configureImplementations(model) implementationConfigurator.configureImplementations(model)
val implementations = model.elements.flatMap { it.implementations } val implementations = model.elements.flatMap { it.implementations }
InterfaceAndAbstractClassConfigurator((model.elements + implementations)) InterfaceAndAbstractClassConfigurator((model.elements + implementations))
.configureInterfacesAndAbstractClasses() .configureInterfacesAndAbstractClasses()
@@ -117,17 +117,15 @@ fun <Element, Implementation, ElementField, ImplementationField> generateTree(
} }
} }
if (createImplementationPrinter != null) { implementations.filter { it.doPrint }.mapTo(generatedFiles) { implementation ->
implementations.mapTo(generatedFiles) { implementation -> printGeneratedType(
printGeneratedType( generationPath,
generationPath, treeGeneratorReadme,
treeGeneratorReadme, implementation.packageName,
implementation.packageName, implementation.typeName,
implementation.typeName, fileSuppressions = listOf("DuplicatedCode", "unused"),
fileSuppressions = listOf("DuplicatedCode", "unused"), ) {
) { createImplementationPrinter(this).printImplementation(implementation)
createImplementationPrinter(this).printImplementation(implementation)
}
} }
} }
@@ -16,7 +16,7 @@ object ImplementationConfigurator : AbstractSwiftIrTreeImplementationConfigurato
override fun configureAllImplementations(model: Model) { override fun configureAllImplementations(model: Model) {
// Use configureFieldInAllImplementations to customize certain fields in all implementation classes // Use configureFieldInAllImplementations to customize certain fields in all implementation classes
configureFieldInAllImplementations( configureFieldInAllImplementations(
field = "parent", fieldName = "parent",
) { ) {
isMutable(it) isMutable(it)
isLateinit(it) isLateinit(it)