[IR generator] Better support for fields' default value in base classes

- Allows using those with the incoming IR implementation printer.
- Moves the logic to common tree generator, so other trees can specify
default values in base classes as well, if needed.

^KT-65773 In Progress
This commit is contained in:
Wojciech Litewka
2024-02-15 19:11:25 +01:00
committed by Space Team
parent 288351d733
commit 4d9d1974ec
9 changed files with 56 additions and 30 deletions
@@ -11,6 +11,7 @@ package org.jetbrains.kotlin.ir.declarations
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrSetValue
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
@@ -32,6 +33,14 @@ abstract class IrVariable : IrDeclarationBase(), IrValueDeclaration {
abstract var initializer: IrExpression?
/**
* Variables are assignable by default. This means that they can be used in [IrSetValue].
* Variables are assigned in the IR even though they are not 'var' in the input. Hence
* the separate assignability flag.
*/
override val isAssignable: Boolean
get() = true
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitVariable(this, data)
@@ -53,12 +53,6 @@ class IrVariableImpl(
override var initializer: IrExpression? = null
// Variables are assignable by default. This means that they can be used in IrSetValue.
// Variables are assigned in the IR even though they are not 'var' in the input. Hence
// the separate assignability flag.
override val isAssignable: Boolean
get() = true
override val factory: IrFactory
get() = error("Create IrVariableImpl directly")
}
@@ -587,6 +587,16 @@ object IrTree : AbstractTreeBuilder() {
+field("isConst", boolean)
+field("isLateinit", boolean)
+field("initializer", expression, nullable = true)
+field("isAssignable", boolean, mutable = false) {
defaultValueInBase = "true"
withGetter = true
additionalImports.add(setValue)
kDoc = """
Variables are assignable by default. This means that they can be used in [${setValue.typeName}].
Variables are assigned in the IR even though they are not 'var' in the input. Hence
the separate assignability flag.
""".trimIndent()
}
}
val packageFragment: Element by element(Declaration) {
ownsChildren = false
@@ -608,8 +618,9 @@ object IrTree : AbstractTreeBuilder() {
}
+field("packageFqName", type<FqName>())
+field("fqName", type<FqName>()) {
baseGetter = "packageFqName"
defaultValueInBase = "packageFqName"
customSetter = "packageFqName = value"
withGetter = true
deprecation = Deprecated(
"Please use `packageFqName` instead",
ReplaceWith("packageFqName"),
@@ -12,8 +12,6 @@ sealed class Field(
override val name: String,
override var isMutable: Boolean,
) : AbstractField<Field>(), AbstractFieldWithDefaultValue<Field> {
var baseGetter: String? = null
sealed class UseFieldAsParameterInIrFactoryStrategy {
data object No : UseFieldAsParameterInIrFactoryStrategy()
@@ -31,28 +29,26 @@ sealed class Field(
UseFieldAsParameterInIrFactoryStrategy.Yes(null)
}
override var withGetter: Boolean
get() = baseGetter != null
set(value) = error("Operation not supported")
override var defaultValueInImplementation: String? by ::baseGetter
override var withGetter: Boolean = false
override var defaultValueInBase: String? = null
override var defaultValueInImplementation: String? = null
override var defaultValueInBuilder: String?
get() = null
set(_) = error("Builders are not supported")
override var customSetter: String? = null
override val origin: Field
get() = this
override var customSetter: String? = null
override fun toString() = "$name: $typeRef"
override val isVolatile: Boolean
get() = false
override val isFinal: Boolean
get() = baseGetter != null
override var isFinal: Boolean = false
override val isParameter: Boolean
get() = false
@@ -61,7 +57,9 @@ sealed class Field(
override fun updateFieldsInCopy(copy: Field) {
super.updateFieldsInCopy(copy)
copy.baseGetter = baseGetter
copy.withGetter = withGetter
copy.defaultValueInBase = defaultValueInBase
copy.defaultValueInImplementation = defaultValueInImplementation
copy.customUseInIrFactoryStrategy = customUseInIrFactoryStrategy
copy.customSetter = customSetter
}
@@ -52,14 +52,15 @@ abstract class AbstractElementPrinter<Element : AbstractElement<Element, Field,
withIndent {
for (field in filterFields(element)) {
if (field.isParameter) continue
if (!field.withGetter && field.defaultValueInImplementation == null && field.isFinal && field.fromParent) {
if (!field.withGetter && field.defaultValueInImplementation == null && field.defaultValueInBase == null && field.isFinal && field.fromParent) {
continue
}
if (separateFieldsWithBlankLine) println()
fieldPrinter.printField(
field,
inImplementation = false,
override = field.fromParent,
modality = Modality.ABSTRACT.takeIf { !field.isFinal && !kind.isInterface },
modality = Modality.ABSTRACT.takeIf { !field.isFinal && !kind.isInterface && field.defaultValueInBase == null },
)
}
printAdditionalMethods(element)
@@ -49,6 +49,7 @@ abstract class AbstractField<Field : AbstractField<Field>> {
open val containsElement: Boolean
get() = typeRef is ElementOrRef<*> || this is ListField && baseType is ElementOrRef<*>
open val defaultValueInBase: String? get() = null
open val defaultValueInImplementation: String? get() = null
/**
@@ -8,8 +8,8 @@ package org.jetbrains.kotlin.generators.tree
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.generators.tree.printer.VariableKind
import org.jetbrains.kotlin.generators.tree.printer.printBlock
import org.jetbrains.kotlin.generators.tree.printer.printKDoc
import org.jetbrains.kotlin.generators.tree.printer.printPropertyDeclaration
import org.jetbrains.kotlin.generators.tree.printer.printKDoc
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
import org.jetbrains.kotlin.utils.SmartPrinter
import org.jetbrains.kotlin.utils.withIndent
@@ -33,12 +33,13 @@ abstract class AbstractFieldPrinter<Field : AbstractField<*>>(
context(ImportCollector)
fun printField(
field: Field,
inImplementation: Boolean,
override: Boolean,
inConstructor: Boolean = false,
modality: Modality? = null,
) {
printer.run {
val defaultValue = field.defaultValueInImplementation
val defaultValue = if (inImplementation) field.defaultValueInImplementation else field.defaultValueInBase
printPropertyDeclaration(
name = field.name,
type = actualTypeOfField(field),
@@ -47,8 +48,8 @@ abstract class AbstractFieldPrinter<Field : AbstractField<*>>(
visibility = field.visibility,
modality = modality,
override = override,
isLateinit = field.isLateinit,
isVolatile = field.isVolatile,
isLateinit = (inImplementation || field.isFinal) && field.isLateinit,
isVolatile = (inImplementation || field.isFinal) && field.isVolatile,
optInAnnotation = field.optInAnnotation,
printOptInWrapped = defaultValue != null,
deprecation = field.deprecation,
@@ -68,11 +68,12 @@ abstract class AbstractImplementation<Implementation, Element, Field>(
return allFields.firstOrNull { it.name == fieldName }
}
private fun withDefault(field: Field) = !field.isFinal && (field.defaultValueInImplementation != null || field.isLateinit)
private fun withDefault(field: Field) =
!field.isFinal && (field.defaultValueInImplementation != null || field.defaultValueInBase != null || field.isLateinit)
val fieldsInConstructor by lazy { allFields.filterNot(::withDefault) }
val fieldsInBody by lazy { allFields.filter(::withDefault) }
val fieldsInBody by lazy { allFields.filter(::withDefault).filter { it.defaultValueInBase == null } }
var requiresOptIn = false
@@ -28,7 +28,8 @@ abstract class AbstractImplementationPrinter<Implementation, Element, Implementa
protected abstract fun makeFieldPrinter(printer: SmartPrinter): AbstractFieldPrinter<ImplementationField>
context(ImportCollector)
protected open fun SmartPrinter.printAdditionalMethods(implementation: Implementation) {}
protected open fun SmartPrinter.printAdditionalMethods(implementation: Implementation) {
}
context(ImportCollector)
fun printImplementation(implementation: Implementation) {
@@ -72,7 +73,7 @@ abstract class AbstractImplementationPrinter<Implementation, Element, Implementa
print(field.name, ": ", field.typeRef.render())
println(",")
} else if (!field.isFinal) {
fieldPrinter.printField(field, override = true, inConstructor = true)
fieldPrinter.printField(field, inImplementation = true, override = true, inConstructor = true)
}
}
}
@@ -87,11 +88,20 @@ abstract class AbstractImplementationPrinter<Implementation, Element, Implementa
printBlock {
if (isInterface || isAbstract) {
implementation.allFields.forEach {
fieldPrinter.printField(it, override = true, modality = Modality.ABSTRACT.takeIf { isAbstract })
fieldPrinter.printField(
it,
inImplementation = true,
override = true,
modality = Modality.ABSTRACT.takeIf { isAbstract }
)
}
} else {
implementation.fieldsInBody.forEach {
fieldPrinter.printField(it, override = true)
fieldPrinter.printField(
it,
inImplementation = true,
override = true
)
}
}