[FIR] FirFakeOverrideGenerator: generate fake declaration with STATUS phase

The requirement for original declarations is to have resolved status,
so we can set at least `STATUS` phase for copied ones. This fixes the
problem that fake declarations for local declarations can have `RAW_FIR`
phase due to the copy from the original declarations, as during body
resolution all local declarations have only this phase.
Another issue: we cannot set the phase from the original declaration
as it is not guaranteed that we will have everything in the resolved
state. E.g., we can have the original declaration in `BODY_RESOLVE`
phase, but at the same time a containing class for fake declaration
can be in `STATUS` phase, for example. This means that the fake
declaration can have unresolved type annotation as it has them
from both places – from the original declaration and from the class
super type.
To simplify the code, we can just always set STATUS phase to be sure
that everything is resolved correctly.

Also, so we can optimize the logic for all phases above on Low Level FIR
level

^KT-64243
This commit is contained in:
Dmitrii Gridin
2023-12-14 16:45:32 +01:00
committed by Space Team
parent 835a9632b9
commit c3e6dd12a2
6 changed files with 206 additions and 181 deletions
@@ -59,37 +59,27 @@ internal object LLFirResolveMultiDesignationCollector {
else -> throwUnexpectedFirElementError(this)
}
private fun FirDeclaration.shouldBeResolved(): Boolean = when (origin) {
is FirDeclarationOrigin.Source,
is FirDeclarationOrigin.ImportedFromObjectOrStatic,
is FirDeclarationOrigin.Delegated,
is FirDeclarationOrigin.Synthetic,
is FirDeclarationOrigin.SubstitutionOverride,
is FirDeclarationOrigin.SamConstructor,
is FirDeclarationOrigin.WrappedIntegerOperator,
is FirDeclarationOrigin.IntersectionOverride,
is FirDeclarationOrigin.ScriptCustomization,
-> {
when (this) {
is FirFile -> true
is FirSyntheticProperty, is FirSyntheticPropertyAccessor -> false
is FirSimpleFunction,
is FirProperty,
is FirPropertyAccessor,
is FirField,
is FirTypeAlias,
is FirConstructor,
-> true
else -> true
}
}
else -> {
private fun FirDeclaration.shouldBeResolved(): Boolean {
if (!origin.isLazyResolvable) {
@OptIn(ResolveStateAccess::class)
check(resolvePhase == FirResolvePhase.BODY_RESOLVE) {
"Expected body resolve phase for origin $origin but found $resolveState"
}
false
return false
}
return when (this) {
is FirFile -> true
is FirSyntheticProperty, is FirSyntheticPropertyAccessor -> false
is FirSimpleFunction,
is FirProperty,
is FirPropertyAccessor,
is FirField,
is FirTypeAlias,
is FirConstructor,
-> true
else -> true
}
}
}
@@ -153,6 +153,9 @@ private class LLFirAnnotationArgumentsTargetResolver(
}
override fun doLazyResolveUnderLock(target: FirElementWithResolveState) {
// There is no sense to resolve such declarations as they do not have their own annotations
if (target is FirCallableDeclaration && target.isCopyCreatedInScope) return
resolveWithKeeper(
target,
target.llFirSession,
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.contracts.FirRawContractDescription
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.FirPrimaryConstructor
import org.jetbrains.kotlin.fir.isCopyCreatedInScope
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirResolveContextCollector
@@ -64,6 +65,9 @@ private class LLFirContractsTargetResolver(
override fun doLazyResolveUnderLock(target: FirElementWithResolveState) {
collectTowerDataContext(target)
// There is no sense to resolve such declarations as they do not have contracts
if (target is FirCallableDeclaration && target.isCopyCreatedInScope) return
when (target) {
is FirPrimaryConstructor, is FirErrorPrimaryConstructor -> {
// No contracts here
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.FirElementWithResolveState
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.isCopyCreatedInScope
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirResolveContextCollector
@@ -73,12 +74,14 @@ private class LLFirExpectActualMatchingTargetResolver(
}
}
private fun FirMemberDeclaration.canHaveExpectCounterPart(): Boolean = when (this) {
is FirEnumEntry -> true
is FirProperty -> true
is FirConstructor -> true
is FirSimpleFunction -> true
is FirRegularClass -> true
is FirTypeAlias -> true
private fun FirMemberDeclaration.canHaveExpectCounterPart(): Boolean = when {
// We shouldn't try to calculate expect/actual mapping for fake declarations
this is FirCallableDeclaration && isCopyCreatedInScope -> false
this is FirEnumEntry -> true
this is FirProperty -> true
this is FirConstructor -> true
this is FirSimpleFunction -> true
this is FirRegularClass -> true
this is FirTypeAlias -> true
else -> false
}
@@ -120,28 +120,24 @@ object FirFakeOverrideGenerator {
newModality: Modality? = null,
newVisibility: Visibility? = null,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution? = null,
): FirSimpleFunction {
checkStatusIsResolved(baseFunction)
): FirSimpleFunction = buildSimpleFunction {
source = derivedClassLookupTag?.toSymbol(session)?.source ?: baseFunction.source
moduleData = session.nullableModuleData ?: baseFunction.moduleData
this.origin = origin
name = baseFunction.name
status = baseFunction.status.copy(newVisibility, newModality, isExpect = isExpect)
symbol = newSymbol
resolvePhase = origin.resolvePhaseForCopy
return buildSimpleFunction {
source = derivedClassLookupTag?.toSymbol(session)?.source ?: baseFunction.source
moduleData = session.nullableModuleData ?: baseFunction.moduleData
this.origin = origin
name = baseFunction.name
status = baseFunction.status.copy(newVisibility, newModality, isExpect = isExpect)
symbol = newSymbol
resolvePhase = baseFunction.resolvePhase
dispatchReceiverType = newDispatchReceiverType
attributes = baseFunction.attributes.copy()
typeParameters += configureAnnotationsTypeParametersAndSignature(
session, baseFunction, newParameterTypes, newTypeParameters,
newReceiverType, newContextReceiverTypes, newReturnType, callableCopySubstitutionForTypeUpdater, newSymbol
).filterIsInstance<FirTypeParameter>()
deprecationsProvider = baseFunction.deprecationsProvider
}.apply {
containingClassForStaticMemberAttr = derivedClassLookupTag.takeIf { shouldOverrideSetContainingClass(baseFunction) }
}
dispatchReceiverType = newDispatchReceiverType
attributes = baseFunction.attributes.copy()
typeParameters += configureAnnotationsTypeParametersAndSignature(
session, baseFunction, newParameterTypes, newTypeParameters,
newReceiverType, newContextReceiverTypes, newReturnType, callableCopySubstitutionForTypeUpdater, newSymbol
).filterIsInstance<FirTypeParameter>()
deprecationsProvider = baseFunction.deprecationsProvider
}.apply {
containingClassForStaticMemberAttr = derivedClassLookupTag.takeIf { shouldOverrideSetContainingClass(baseFunction) }
}
fun createCopyForFirConstructor(
@@ -157,46 +153,42 @@ object FirFakeOverrideGenerator {
newTypeParameters: List<FirTypeParameterRef>?,
isExpect: Boolean,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution?,
): FirConstructor {
checkStatusIsResolved(baseConstructor)
): FirConstructor = buildConstructor {
// TODO: consider using here some light-weight functions instead of pseudo-real FirMemberFunctionImpl
// As second alternative, we can invent some light-weight kind of FirRegularClass
return buildConstructor {
annotations += baseConstructor.annotations
source = derivedClassLookupTag?.toSymbol(session)?.source ?: baseConstructor.source
moduleData = session.nullableModuleData ?: baseConstructor.moduleData
this.origin = origin
receiverParameter = baseConstructor.receiverParameter?.let { receiverParameter ->
buildReceiverParameterCopy(receiverParameter) {
typeRef = receiverParameter.typeRef.withReplacedConeType(null)
}
annotations += baseConstructor.annotations
source = derivedClassLookupTag?.toSymbol(session)?.source ?: baseConstructor.source
moduleData = session.nullableModuleData ?: baseConstructor.moduleData
this.origin = origin
receiverParameter = baseConstructor.receiverParameter?.let { receiverParameter ->
buildReceiverParameterCopy(receiverParameter) {
typeRef = receiverParameter.typeRef.withReplacedConeType(null)
}
status = baseConstructor.status.copy(isExpect = isExpect)
symbol = fakeOverrideSymbol
typeParameters += configureAnnotationsTypeParametersAndSignature(
session,
baseConstructor,
newParameterTypes,
newTypeParameters,
newReceiverType = null,
newContextReceiverTypes,
newReturnType,
callableCopySubstitutionForTypeUpdater,
fakeOverrideSymbol
)
dispatchReceiverType = newDispatchReceiverType
resolvePhase = baseConstructor.resolvePhase
attributes = baseConstructor.attributes.copy()
deprecationsProvider = baseConstructor.deprecationsProvider
}.apply {
originalForSubstitutionOverrideAttr = baseConstructor
containingClassForStaticMemberAttr = derivedClassLookupTag.takeIf { shouldOverrideSetContainingClass(baseConstructor) }
}
status = baseConstructor.status.copy(isExpect = isExpect)
symbol = fakeOverrideSymbol
typeParameters += configureAnnotationsTypeParametersAndSignature(
session,
baseConstructor,
newParameterTypes,
newTypeParameters,
newReceiverType = null,
newContextReceiverTypes,
newReturnType,
callableCopySubstitutionForTypeUpdater,
fakeOverrideSymbol
)
dispatchReceiverType = newDispatchReceiverType
resolvePhase = origin.resolvePhaseForCopy
attributes = baseConstructor.attributes.copy()
deprecationsProvider = baseConstructor.deprecationsProvider
}.apply {
originalForSubstitutionOverrideAttr = baseConstructor
containingClassForStaticMemberAttr = derivedClassLookupTag.takeIf { shouldOverrideSetContainingClass(baseConstructor) }
}
private fun FirFunctionBuilder.configureAnnotationsTypeParametersAndSignature(
@@ -277,6 +269,7 @@ object FirFakeOverrideGenerator {
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution?,
origin: FirDeclarationOrigin,
) {
checkStatusIsResolved(baseFunction)
annotations += baseFunction.annotations
@Suppress("NAME_SHADOWING")
@@ -338,6 +331,8 @@ object FirFakeOverrideGenerator {
coneTypeOrNull = returnTypeRef.coneTypeOrNull
}
}
resolvePhase = origin.resolvePhaseForCopy
}.apply {
addOverrideAttributeIfNeeded(original)
}
@@ -397,55 +392,51 @@ object FirFakeOverrideGenerator {
newModality: Modality? = null,
newVisibility: Visibility? = null,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution? = null,
): FirProperty {
checkStatusIsResolved(baseProperty)
): FirProperty = buildProperty {
source = derivedClassLookupTag?.toSymbol(session)?.source ?: baseProperty.source
moduleData = session.nullableModuleData ?: baseProperty.moduleData
this.origin = origin
name = baseProperty.name
isVar = baseProperty.isVar
this.symbol = newSymbol
isLocal = false
status = baseProperty.status.copy(newVisibility, newModality, isExpect = isExpect)
return buildProperty {
source = derivedClassLookupTag?.toSymbol(session)?.source ?: baseProperty.source
moduleData = session.nullableModuleData ?: baseProperty.moduleData
this.origin = origin
name = baseProperty.name
isVar = baseProperty.isVar
this.symbol = newSymbol
isLocal = false
status = baseProperty.status.copy(newVisibility, newModality, isExpect = isExpect)
resolvePhase = origin.resolvePhaseForCopy
dispatchReceiverType = newDispatchReceiverType
attributes = baseProperty.attributes.copy()
typeParameters += configureAnnotationsTypeParametersAndSignature(
session,
baseProperty,
newTypeParameters,
newReceiverType,
newContextReceiverTypes,
newReturnType,
callableCopySubstitutionForTypeUpdater
)
deprecationsProvider = baseProperty.deprecationsProvider
resolvePhase = baseProperty.resolvePhase
dispatchReceiverType = newDispatchReceiverType
attributes = baseProperty.attributes.copy()
typeParameters += configureAnnotationsTypeParametersAndSignature(
session,
baseProperty,
newTypeParameters,
newReceiverType,
newContextReceiverTypes,
newReturnType,
callableCopySubstitutionForTypeUpdater
)
deprecationsProvider = baseProperty.deprecationsProvider
getter = baseProperty.getter?.buildCopyIfNeeded(
moduleData = session.nullableModuleData ?: baseProperty.moduleData,
origin = origin,
propertyReturnTypeRef = this@buildProperty.returnTypeRef,
propertySymbol = newSymbol,
dispatchReceiverType = dispatchReceiverType,
derivedClassLookupTag = derivedClassLookupTag,
baseProperty = baseProperty,
)
getter = baseProperty.getter?.buildCopyIfNeeded(
moduleData = session.nullableModuleData ?: baseProperty.moduleData,
origin = origin,
propertyReturnTypeRef = this@buildProperty.returnTypeRef,
propertySymbol = newSymbol,
dispatchReceiverType = dispatchReceiverType,
derivedClassLookupTag = derivedClassLookupTag,
baseProperty = baseProperty,
)
setter = baseProperty.setter?.buildCopyIfNeeded(
moduleData = session.nullableModuleData ?: baseProperty.moduleData,
origin = origin,
propertyReturnTypeRef = this@buildProperty.returnTypeRef,
propertySymbol = newSymbol,
dispatchReceiverType = dispatchReceiverType,
derivedClassLookupTag = derivedClassLookupTag,
baseProperty = baseProperty,
)
}.apply {
containingClassForStaticMemberAttr = derivedClassLookupTag.takeIf { shouldOverrideSetContainingClass(baseProperty) }
}
setter = baseProperty.setter?.buildCopyIfNeeded(
moduleData = session.nullableModuleData ?: baseProperty.moduleData,
origin = origin,
propertyReturnTypeRef = this@buildProperty.returnTypeRef,
propertySymbol = newSymbol,
dispatchReceiverType = dispatchReceiverType,
derivedClassLookupTag = derivedClassLookupTag,
baseProperty = baseProperty,
)
}.apply {
containingClassForStaticMemberAttr = derivedClassLookupTag.takeIf { shouldOverrideSetContainingClass(baseProperty) }
}
private fun FirPropertyAccessor.buildCopyIfNeeded(
@@ -487,7 +478,7 @@ object FirFakeOverrideGenerator {
propertySymbol = propertySymbol,
modality = modality ?: Modality.FINAL,
effectiveVisibility = effectiveVisibility,
resolvePhase = resolvePhase,
resolvePhase = origin.resolvePhaseForCopy,
).apply {
replaceAnnotations(this@buildCopy.annotations)
}
@@ -500,7 +491,7 @@ object FirFakeOverrideGenerator {
propertySymbol = propertySymbol,
modality = modality ?: Modality.FINAL,
effectiveVisibility = effectiveVisibility,
resolvePhase = resolvePhase,
resolvePhase = origin.resolvePhaseForCopy,
).apply {
replaceAnnotations(this@buildCopy.annotations)
}
@@ -511,6 +502,7 @@ object FirFakeOverrideGenerator {
this.propertySymbol = propertySymbol
this.dispatchReceiverType = dispatchReceiverType
this.body = null
resolvePhase = origin.resolvePhaseForCopy
}.also {
if (it.isSetter) {
val originalParameter = it.valueParameters.first()
@@ -547,27 +539,25 @@ object FirFakeOverrideGenerator {
newModality: Modality? = null,
newVisibility: Visibility? = null,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution? = null,
): FirField {
return buildField {
source = baseField.source
moduleData = session.nullableModuleData ?: baseField.moduleData
this.origin = origin
name = baseField.name
isVar = baseField.isVar
this.symbol = newSymbol
status = baseField.status.copy(newVisibility, newModality, isExpect = isExpect)
): FirField = buildField {
source = baseField.source
moduleData = session.nullableModuleData ?: baseField.moduleData
this.origin = origin
name = baseField.name
isVar = baseField.isVar
this.symbol = newSymbol
status = baseField.status.copy(newVisibility, newModality, isExpect = isExpect)
resolvePhase = baseField.resolvePhase
dispatchReceiverType = newDispatchReceiverType
attributes = baseField.attributes.copy()
configureAnnotationsAndSignature(
baseField, newReceiverType, newContextReceiverTypes, newReturnType,
callableCopySubstitutionForTypeUpdater, updateReceiver = false
)
deprecationsProvider = baseField.deprecationsProvider
}.apply {
containingClassForStaticMemberAttr = derivedClassLookupTag.takeIf { shouldOverrideSetContainingClass(baseField) }
}
resolvePhase = origin.resolvePhaseForCopy
dispatchReceiverType = newDispatchReceiverType
attributes = baseField.attributes.copy()
configureAnnotationsAndSignature(
baseField, newReceiverType, newContextReceiverTypes, newReturnType,
callableCopySubstitutionForTypeUpdater, updateReceiver = false
)
deprecationsProvider = baseField.deprecationsProvider
}.apply {
containingClassForStaticMemberAttr = derivedClassLookupTag.takeIf { shouldOverrideSetContainingClass(baseField) }
}
private fun FirPropertyBuilder.configureAnnotationsTypeParametersAndSignature(
@@ -652,6 +642,7 @@ object FirFakeOverrideGenerator {
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution?,
updateReceiver: Boolean = true,
) {
checkStatusIsResolved(baseVariable)
annotations += baseVariable.annotations
@Suppress("NAME_SHADOWING")
@@ -690,29 +681,25 @@ object FirFakeOverrideGenerator {
derivedClassLookupTag: ConeClassLikeLookupTag,
newReturnType: ConeKotlinType?,
origin: FirDeclarationOrigin.SubstitutionOverride,
): FirFieldSymbol {
): FirFieldSymbol = buildField {
val symbol = FirFieldSymbol(CallableId(derivedClassLookupTag.classId, baseField.name))
buildField {
moduleData = session.nullableModuleData ?: baseField.moduleData
this.symbol = symbol
this.origin = origin
returnTypeRef = baseField.returnTypeRef.withReplacedConeType(newReturnType)
moduleData = session.nullableModuleData ?: baseField.moduleData
this.symbol = symbol
this.origin = origin
returnTypeRef = baseField.returnTypeRef.withReplacedConeType(newReturnType)
source = baseField.source
resolvePhase = baseField.resolvePhase
name = baseField.name
isVar = baseField.isVar
status = baseField.status
resolvePhase = baseField.resolvePhase
annotations += baseField.annotations
attributes = baseField.attributes.copy()
dispatchReceiverType = baseField.dispatchReceiverType
}.apply {
originalForSubstitutionOverrideAttr = baseField
containingClassForStaticMemberAttr = derivedClassLookupTag.takeIf { shouldOverrideSetContainingClass(baseField) }
}
return symbol
}
source = baseField.source
name = baseField.name
isVar = baseField.isVar
status = baseField.status
resolvePhase = origin.resolvePhaseForCopy
annotations += baseField.annotations
attributes = baseField.attributes.copy()
dispatchReceiverType = baseField.dispatchReceiverType
}.apply {
originalForSubstitutionOverrideAttr = baseField
containingClassForStaticMemberAttr = derivedClassLookupTag.takeIf { shouldOverrideSetContainingClass(baseField) }
}.symbol
// Returns a list of type parameters, and a substitutor that should be used for all other types
fun createNewTypeParametersAndSubstitutor(
@@ -730,7 +717,7 @@ object FirFakeOverrideGenerator {
source = typeParameter.source
moduleData = typeParameter.moduleData
this.origin = origin
resolvePhase = FirResolvePhase.DECLARATIONS
resolvePhase = origin.resolvePhaseForCopy
name = typeParameter.name
symbol = FirTypeParameterSymbol()
variance = typeParameter.variance
@@ -794,4 +781,24 @@ object FirFakeOverrideGenerator {
withEntry("declarationStatus", member.status) { it.toString() }
}
}
/**
* In Low Level FIR we cannot be sure that all copied elements are already resolved,
* so we play safe with [FirResolvePhase.STATUS] phase in such cases.
* Example:
* ```kotlin
* class MyClass : BaseClass<@Anno("super $constant") Int>()
*
* abstract class BaseClass<T : @Anno("bound $constant") Number> {
* var property: SCHEME
* }
* ```
* here we can have `BaseClass.property` already in [FirResolvePhase.BODY_RESOLVE] phase,
* but `MyClass` in [FirResolvePhase.STATUS].
* So [FirResolvePhase.BODY_RESOLVE] on the fake override will lead to the problem because
* effectively we will have unresolved `@Anno("super $constant")` annotation inside "fully resolve"
* function
*/
private val FirDeclarationOrigin.resolvePhaseForCopy: FirResolvePhase
get() = if (isLazyResolvable) FirResolvePhase.STATUS else FirResolvePhase.BODY_RESOLVE
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2023 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.
*/
@@ -63,3 +63,21 @@ sealed class FirDeclarationOrigin(
val GeneratedDeclarationKey.origin: FirDeclarationOrigin
get() = FirDeclarationOrigin.Plugin(this)
/**
* @return **true** if a declaration with [this] origin can be in not fully resolved state
*/
val FirDeclarationOrigin.isLazyResolvable: Boolean
get() = when (this) {
is FirDeclarationOrigin.Source,
is FirDeclarationOrigin.ImportedFromObjectOrStatic,
is FirDeclarationOrigin.Delegated,
is FirDeclarationOrigin.Synthetic,
is FirDeclarationOrigin.SubstitutionOverride,
is FirDeclarationOrigin.SamConstructor,
is FirDeclarationOrigin.WrappedIntegerOperator,
is FirDeclarationOrigin.IntersectionOverride,
is FirDeclarationOrigin.ScriptCustomization,
-> true
else -> false
}