From 38079372e198151c11b44a18f991a7cf8ad816a7 Mon Sep 17 00:00:00 2001 From: Nikolay Lunyak Date: Thu, 5 Aug 2021 11:45:04 +0300 Subject: [PATCH] [FIR] Add RawFirBuilder & DeclarationsConverter support --- .../converter/DeclarationsConverter.kt | 101 ++++++++++++++---- .../kotlin/fir/builder/RawFirBuilder.kt | 80 ++++++++++++-- 2 files changed, 155 insertions(+), 26 deletions(-) diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt index a46aa6b943f..918d1298537 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt @@ -21,10 +21,7 @@ import org.jetbrains.kotlin.fir.contracts.FirContractDescription import org.jetbrains.kotlin.fir.contracts.builder.buildRawContractDescription import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.builder.* -import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl -import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor -import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyGetter -import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertySetter +import org.jetbrains.kotlin.fir.declarations.impl.* import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind @@ -1041,6 +1038,7 @@ class DeclarationsConverter( val accessors = mutableListOf() var propertyInitializer: FirExpression? = null var typeParameterList: LighterASTNode? = null + var fieldDeclaration: LighterASTNode? = null property.forEachChildren { when (it.tokenType) { MODIFIER_LIST -> modifiers = convertModifierList(it) @@ -1054,6 +1052,7 @@ class DeclarationsConverter( PROPERTY_ACCESSOR -> { accessors += it } + BACKING_FIELD -> fieldDeclaration = it else -> if (it.isExpression()) propertyInitializer = expressionConverter.getAsFirExpression(it, "Should have initializer") } } @@ -1082,6 +1081,8 @@ class DeclarationsConverter( typeParameterList?.let { firTypeParameters += convertTypeParameters(it, typeConstraints, symbol) } + backingField = fieldDeclaration.convertBackingField(symbol, modifiers) + if (isLocal) { this.isLocal = true val delegateBuilder = delegateExpression?.let { @@ -1130,21 +1131,24 @@ class DeclarationsConverter( isExternal = modifiers.hasExternal() } - val convertedAccessors = accessors.map { convertGetterOrSetter(it, returnType, propertyVisibility, symbol, modifiers) } + val hasExplicitBackingField = backingField != null + val convertedAccessors = accessors.map { + convertGetterOrSetter(it, returnType, propertyVisibility, symbol, modifiers, hasExplicitBackingField) + } this.getter = convertedAccessors.find { it.isGetter } - ?: FirDefaultPropertyGetter( - property.toFirSourceElement(FirFakeSourceElementKind.DefaultAccessor), - moduleData, - FirDeclarationOrigin.Source, - returnType, - propertyVisibility, symbol, - ).also { - it.status = defaultAccessorStatus() - it.initContainingClassAttr() + ?: if (!hasExplicitBackingField) { + FirDefaultPropertyGetter( + property.toFirSourceElement(FirFakeSourceElementKind.DefaultAccessor), moduleData, FirDeclarationOrigin.Source, returnType, propertyVisibility, symbol, + ).also { + it.status = defaultAccessorStatus() + it.initContainingClassAttr() + } + } else { + null } // NOTE: We still need the setter even for a val property so we can report errors (e.g., VAL_WITH_SETTER). this.setter = convertedAccessors.find { it.isSetter } - ?: if (isVar) { + ?: if (isVar && !hasExplicitBackingField) { FirDefaultPropertySetter( property.toFirSourceElement(FirFakeSourceElementKind.DefaultAccessor), moduleData, @@ -1246,14 +1250,15 @@ class DeclarationsConverter( } /** - * @see org.jetbrains.kotlin.parsing.KotlinParsing.parsePropertyGetterOrSetter + * @see org.jetbrains.kotlin.parsing.KotlinParsing.parsePropertyComponent */ private fun convertGetterOrSetter( getterOrSetter: LighterASTNode, propertyTypeRef: FirTypeRef, propertyVisibility: Visibility, propertySymbol: FirPropertySymbol, - propertyModifiers: Modifier + propertyModifiers: Modifier, + hasBackingField: Boolean, ): FirPropertyAccessor { var modifiers = Modifier() var isGetter = true @@ -1292,7 +1297,11 @@ class DeclarationsConverter( isExternal = propertyModifiers.hasExternal() || modifiers.hasExternal() } val sourceElement = getterOrSetter.toFirSourceElement() - if (block == null && expression == null) { + // If an explicit backing field is present, + // the default accessors might not be compatible + // with it. We should check the types first, and + // only then see if we can create the default accessors. + if (block == null && expression == null && !hasBackingField) { return FirDefaultPropertyAccessor .createGetterOrSetter( sourceElement, @@ -1340,6 +1349,62 @@ class DeclarationsConverter( } } + /** + * @see org.jetbrains.kotlin.parsing.KotlinParsing.parsePropertyComponent + */ + private fun LighterASTNode?.convertBackingField( + propertySymbol: FirPropertySymbol, + propertyModifiers: Modifier, + ): FirBackingField? { + var modifiers = Modifier() + var returnType: FirTypeRef = implicitType + var initializerExpression: LighterASTNode? = null + this?.forEachChildren { + when { + it.tokenType == MODIFIER_LIST -> modifiers = convertModifierList(it) + it.tokenType == TYPE_REFERENCE -> returnType = convertType(it) + it.isExpression() -> initializerExpression = it + } + } + val backingFieldInitializer: FirExpression = expressionConverter.getAsFirExpression( + initializerExpression, + "Should have initializer" + ) + var componentVisibility = modifiers.getVisibility() + if (componentVisibility == Visibilities.Unknown) { + componentVisibility = Visibilities.Private + } + val status = obtainPropertyComponentStatus(componentVisibility, modifiers, propertyModifiers) + val sourceElement = this?.toFirSourceElement() + return if (this != null) { + buildBackingField { + source = sourceElement + moduleData = baseModuleData + origin = FirDeclarationOrigin.Source + returnTypeRef = returnType + symbol = FirPropertyFieldDeclarationSymbol() + this.status = status + annotations += modifiers.annotations + this.propertySymbol = propertySymbol + this.initializer = backingFieldInitializer + } + } else { + null + } + } + + private fun obtainPropertyComponentStatus( + componentVisibility: Visibility, + modifiers: Modifier, + propertyModifiers: Modifier, + ): FirDeclarationStatus { + // Downward propagation of `inline` and `external` modifiers (from property to its accessors) + return FirDeclarationStatusImpl(componentVisibility, modifiers.getModality(isClassOrObject = false)).apply { + isInline = propertyModifiers.hasInline() || modifiers.hasInline() + isExternal = propertyModifiers.hasExternal() || modifiers.hasExternal() + } + } + private fun obtainContractDescription(rawContractDescription: LighterASTNode): FirContractDescription? = buildRawContractDescription { source = rawContractDescription.toFirSourceElement() diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index 40e917b5711..9d5e44058df 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -18,10 +18,7 @@ import org.jetbrains.kotlin.fir.contracts.FirContractDescription import org.jetbrains.kotlin.fir.contracts.builder.buildRawContractDescription import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.builder.* -import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl -import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor -import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyGetter -import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertySetter +import org.jetbrains.kotlin.fir.declarations.impl.* import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.diagnostics.* import org.jetbrains.kotlin.fir.expressions.* @@ -370,6 +367,7 @@ open class RawFirBuilder( propertyTypeRef: FirTypeRef, propertySymbol: FirPropertySymbol, isGetter: Boolean, + hasExplicitBackingField: Boolean, ): FirPropertyAccessor? { val accessorVisibility = if (this?.visibility != null && this.visibility != Visibilities.Unknown) this.visibility else property.visibility @@ -426,7 +424,11 @@ open class RawFirBuilder( this@RawFirBuilder.context.firFunctionTargets.removeLast() } } - isGetter || property.isVar -> { + // If an explicit backing field is present, + // the default accessors might not be compatible + // with it. We should check the types first, and + // only then see if we can create the default accessors. + !hasExplicitBackingField && (isGetter || property.isVar) -> { // Default getter for val/var properties, and default setter for var properties. val propertySource = this?.toFirSourceElement() ?: property.toFirPsiSourceElement(FirFakeSourceElementKind.DefaultAccessor) @@ -455,6 +457,55 @@ open class RawFirBuilder( } } + private fun obtainPropertyComponentStatus( + componentVisibility: Visibility, + declaration: KtDeclaration?, + property: KtProperty, + ): FirDeclarationStatus { + return FirDeclarationStatusImpl(componentVisibility, declaration?.modality).apply { + isInline = property.hasModifier(INLINE_KEYWORD) || + declaration?.hasModifier(INLINE_KEYWORD) == true + isExternal = property.hasModifier(EXTERNAL_KEYWORD) || + declaration?.hasModifier(EXTERNAL_KEYWORD) == true + } + } + + private fun KtBackingField?.toFirBackingField( + property: KtProperty, + propertySymbol: FirPropertySymbol, + ): FirBackingField? { + val componentVisibility = if (this?.visibility != null && this.visibility != Visibilities.Unknown) { + this.visibility + } else { + Visibilities.Private + } + val status = obtainPropertyComponentStatus(componentVisibility, this, property) + val backingFieldInitializer = when (mode) { + BodyBuildingMode.LAZY_BODIES -> buildLazyExpression { + source = this@toFirBackingField?.initializer?.toFirSourceElement() + } + BodyBuildingMode.STUBS -> buildExpressionStub() + else -> this@toFirBackingField?.initializer.toFirExpression("Should have initializer") + } + val returnType = this?.returnTypeReference.toFirOrImplicitType() + val source = this?.toFirSourceElement() + return if (this != null) { + buildBackingField { + this.source = source + moduleData = baseModuleData + origin = FirDeclarationOrigin.Source + returnTypeRef = returnType + this.status = status + extractAnnotationsTo(this) + symbol = FirPropertyFieldDeclarationSymbol() + this.propertySymbol = propertySymbol + this.initializer = backingFieldInitializer + } + } else { + null + } + } + private fun KtParameter.toFirValueParameter( defaultTypeRef: FirTypeRef? = null, valueParameterDeclaration: ValueParameterDeclaration = ValueParameterDeclaration.OTHER @@ -1423,6 +1474,12 @@ open class RawFirBuilder( if (this@toFirProperty.isLocal) { isLocal = true symbol = FirPropertySymbol(propertyName) + + backingField = this@toFirProperty.fieldDeclaration.toFirBackingField( + this@toFirProperty, + propertySymbol = symbol, + ) + status = FirDeclarationStatusImpl(Visibilities.Local, Modality.FINAL).apply { isLateInit = hasModifier(LATEINIT_KEYWORD) } @@ -1455,17 +1512,25 @@ open class RawFirBuilder( dispatchReceiverType = currentDispatchReceiverType() extractTypeParametersTo(this, symbol) withCapturedTypeParameters(true, this.typeParameters) { + backingField = this@toFirProperty.fieldDeclaration.toFirBackingField( + this@toFirProperty, + propertySymbol = symbol, + ) + + val hasExplicitBackingField = backingField != null getter = this@toFirProperty.getter.toFirPropertyAccessor( this@toFirProperty, propertyType, propertySymbol = symbol, - isGetter = true + isGetter = true, + hasExplicitBackingField, ) setter = this@toFirProperty.setter.toFirPropertyAccessor( this@toFirProperty, propertyType, propertySymbol = symbol, - isGetter = false + isGetter = false, + hasExplicitBackingField, ) status = FirDeclarationStatusImpl(visibility, modality).apply { @@ -2420,4 +2485,3 @@ enum class PsiHandlingMode { */ IDE; } -