[FIR] Refactor deferred computation of callable copy return types

Rename some classes for clarity and move the implementations to the use
sites.
This commit is contained in:
Kirill Rakhman
2024-02-27 12:24:13 +01:00
committed by Space Team
parent aa1cce78c3
commit 8a90032e93
46 changed files with 257 additions and 231 deletions
@@ -5,45 +5,59 @@
package org.jetbrains.kotlin.fir.scopes
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclarationAttributes
import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataKey
import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataRegistry
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
import org.jetbrains.kotlin.fir.scopes.impl.FirTypeIntersectionScopeContext
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.coneTypeOrNull
/**
* A utility class that, depending on the implementation, calculates the return type of callable copies,
* (substitution/intersection overrides, delegated members, and enhanced Java declarations) and returns it.
*
* See [FirDeclarationAttributes.deferredCallableCopyReturnType].
*/
abstract class CallableCopyTypeCalculator {
/**
* Returns the [FirTypeRef] for [FirCallableDeclaration.returnTypeRef] of the [declaration].
*
* Depending on the implementation, this call might invoke a deferred computation of the return type
* (see [FirDeclarationAttributes.deferredCallableCopyReturnType]).
*
* A return value of `null` signifies that the calculation has failed or that no deferred computation was stored
* and the return type could not be resolved ordinarily.
*/
abstract fun computeReturnType(declaration: FirCallableDeclaration): FirTypeRef?
fun computeReturnTypeOrNull(declaration: FirCallableDeclaration): ConeKotlinType? {
return computeReturnType(declaration)?.coneTypeOrNull
}
/**
* Doesn't perform any calculation and returns [FirCallableDeclaration.returnTypeRef].
*/
object DoNothing : CallableCopyTypeCalculator() {
override fun computeReturnType(declaration: FirCallableDeclaration): FirTypeRef {
return declaration.returnTypeRef
}
}
abstract class AbstractCallableCopyTypeCalculator : CallableCopyTypeCalculator() {
/**
* If necessary, runs the computation saved in [FirDeclarationAttributes.deferredCallableCopyReturnType] and returns a [FirResolvedTypeRef].
*/
abstract class DeferredCallableCopyTypeCalculator : CallableCopyTypeCalculator() {
override fun computeReturnType(declaration: FirCallableDeclaration): FirResolvedTypeRef? {
val callableCopyDeferredTypeCalculation = declaration.attributes.callableCopyDeferredTypeCalculation
val callableCopyDeferredTypeCalculation = declaration.attributes.deferredCallableCopyReturnType
?: return declaration.getResolvedTypeRef()
// TODO: drop synchronized in KT-60385
synchronized(callableCopyDeferredTypeCalculation) {
if (declaration.attributes.callableCopyDeferredTypeCalculation == null) {
if (declaration.attributes.deferredCallableCopyReturnType == null) {
return declaration.returnTypeRef as FirResolvedTypeRef
}
val returnType = when (callableCopyDeferredTypeCalculation) {
is CallableCopySubstitution -> computeSubstitution(callableCopyDeferredTypeCalculation)
is CallableCopyIntersection -> computeIntersection(callableCopyDeferredTypeCalculation)
} ?: return null
val returnType = callableCopyDeferredTypeCalculation.computeReturnType(this) ?: return null
val returnTypeRef = declaration.returnTypeRef.resolvedTypeFromPrototype(returnType)
declaration.replaceReturnTypeRef(returnTypeRef)
@@ -52,33 +66,19 @@ abstract class CallableCopyTypeCalculator {
declaration.setter?.valueParameters?.firstOrNull()?.replaceReturnTypeRef(returnTypeRef)
}
declaration.attributes.callableCopyDeferredTypeCalculation = null
declaration.attributes.deferredCallableCopyReturnType = null
return returnTypeRef
}
}
private fun computeSubstitution(callableCopySubstitutionForTypeUpdater: CallableCopySubstitution): ConeKotlinType? {
val (substitutor, baseSymbol) = callableCopySubstitutionForTypeUpdater
val baseDeclaration = baseSymbol.fir as FirCallableDeclaration
val baseReturnType = computeReturnType(baseDeclaration)?.type ?: return null
val coneType = substitutor.substituteOrSelf(baseReturnType)
return coneType
}
private fun computeIntersection(callableCopyIntersection: CallableCopyIntersection): ConeKotlinType? {
val (mostSpecific, session) = callableCopyIntersection
return FirTypeIntersectionScopeContext.intersectReturnTypes(mostSpecific, session) {
computeReturnType(this)?.type
}
}
protected abstract fun FirCallableDeclaration.getResolvedTypeRef(): FirResolvedTypeRef?
}
object Forced : AbstractCallableCopyTypeCalculator() {
/**
* See [DeferredCallableCopyTypeCalculator].
*/
object Forced : DeferredCallableCopyTypeCalculator() {
override fun FirCallableDeclaration.getResolvedTypeRef(): FirResolvedTypeRef? {
return returnTypeRef as? FirResolvedTypeRef
}
@@ -87,24 +87,19 @@ abstract class CallableCopyTypeCalculator {
// ---------------------------------------------------------------------------------------------------------------------------------------
private object CallableCopyDeferredReturnTypeCalculationKey : FirDeclarationDataKey()
private object DeferredCallableCopyReturnTypeKey : FirDeclarationDataKey()
var FirDeclarationAttributes.callableCopyDeferredTypeCalculation: CallableCopyDeferredReturnTypeCalculation? by FirDeclarationDataRegistry.attributesAccessor(
CallableCopyDeferredReturnTypeCalculationKey
var FirDeclarationAttributes.deferredCallableCopyReturnType: DeferredCallableCopyReturnType? by FirDeclarationDataRegistry.attributesAccessor(
DeferredCallableCopyReturnTypeKey
)
sealed class CallableCopyDeferredReturnTypeCalculation
abstract class DeferredCallableCopyReturnType {
/**
* Performs a deferred computation some declaration's return type.
*
* [calc] must be used for the return type calculation of overridden members which might recursively trigger the computation of
* deferred return types.
*/
abstract fun computeReturnType(calc: CallableCopyTypeCalculator): ConeKotlinType?
}
data class CallableCopySubstitution internal constructor(
val substitutor: ConeSubstitutor,
val baseSymbol: FirBasedSymbol<*>
) : CallableCopyDeferredReturnTypeCalculation()
data class CallableCopyIntersection internal constructor(
val mostSpecific: Collection<FirCallableSymbol<*>>,
val session: FirSession,
) : CallableCopyDeferredReturnTypeCalculation() {
override fun toString(): String {
return "CallableCopyIntersection(mostSpecific=$mostSpecific)"
}
}
@@ -16,7 +16,8 @@ import org.jetbrains.kotlin.fir.originalForSubstitutionOverride
import org.jetbrains.kotlin.fir.resolve.ScopeSessionKey
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.chain
import org.jetbrains.kotlin.fir.scopes.CallableCopySubstitution
import org.jetbrains.kotlin.fir.scopes.DeferredCallableCopyReturnType
import org.jetbrains.kotlin.fir.scopes.CallableCopyTypeCalculator
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
@@ -285,7 +286,7 @@ class FirClassSubstitutionScope(
val receiverType: ConeKotlinType?,
val returnType: ConeKotlinType?,
val substitutor: ConeSubstitutor,
val callableCopySubstitution: CallableCopySubstitution?
val deferredReturnTypeOfSubstitution: DeferredReturnTypeOfSubstitution?
)
private fun createSubstitutedData(member: FirCallableDeclaration, symbolForOverride: FirBasedSymbol<*>): SubstitutedData {
@@ -307,7 +308,7 @@ class FirClassSubstitutionScope(
val newDispatchReceiverType = dispatchReceiverTypeForSubstitutedMembers.substitute(substitutor)
val returnType = member.returnTypeRef.coneTypeSafe<ConeKotlinType>()
val callableCopySubstitution = runIf(returnType == null) { CallableCopySubstitution(substitutor, member.symbol) }
val deferredReturnTypeOfSubstitution = runIf(returnType == null) { DeferredReturnTypeOfSubstitution(substitutor, member.symbol) }
val newReturnType = returnType?.substitute(substitutor)
return SubstitutedData(
newTypeParameters,
@@ -315,7 +316,7 @@ class FirClassSubstitutionScope(
newReceiverType,
newReturnType,
substitutor,
callableCopySubstitution
deferredReturnTypeOfSubstitution
)
}
@@ -377,3 +378,20 @@ class FirSubstitutionOverrideStorage(val session: FirSession) : FirSessionCompon
}
private val FirSession.substitutionOverrideStorage: FirSubstitutionOverrideStorage by FirSession.sessionComponentAccessor()
internal class DeferredReturnTypeOfSubstitution(
private val substitutor: ConeSubstitutor,
private val baseSymbol: FirBasedSymbol<*>,
) : DeferredCallableCopyReturnType() {
override fun computeReturnType(calc: CallableCopyTypeCalculator): ConeKotlinType? {
val baseDeclaration = baseSymbol.fir as FirCallableDeclaration
val baseReturnType = calc.computeReturnTypeOrNull(baseDeclaration) ?: return null
val coneType = substitutor.substituteOrSelf(baseReturnType)
return coneType
}
override fun toString(): String {
return "DeferredReturnTypeOfSubstitution(substitutor=$substitutor, baseSymbol=$baseSymbol)"
}
}
@@ -20,9 +20,8 @@ import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.CallableCopyDeferredReturnTypeCalculation
import org.jetbrains.kotlin.fir.scopes.CallableCopySubstitution
import org.jetbrains.kotlin.fir.scopes.callableCopyDeferredTypeCalculation
import org.jetbrains.kotlin.fir.scopes.DeferredCallableCopyReturnType
import org.jetbrains.kotlin.fir.scopes.deferredCallableCopyReturnType
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
@@ -50,7 +49,7 @@ object FirFakeOverrideGenerator {
newParameterTypes: List<ConeKotlinType?>? = null,
newTypeParameters: List<FirTypeParameter>? = null,
isExpect: Boolean = baseFunction.isExpect,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution? = null,
callableCopySubstitutionForTypeUpdater: DeferredCallableCopyReturnType? = null,
): FirNamedFunctionSymbol {
createSubstitutionOverrideFunction(
symbolForSubstitutionOverride, session, baseFunction, derivedClassLookupTag,
@@ -81,7 +80,7 @@ object FirFakeOverrideGenerator {
newParameterTypes: List<ConeKotlinType?>?,
newTypeParameters: List<FirTypeParameter>?,
isExpect: Boolean = baseFunction.isExpect,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution?,
callableCopySubstitutionForTypeUpdater: DeferredCallableCopyReturnType?,
origin: FirDeclarationOrigin.SubstitutionOverride,
): FirSimpleFunction {
// TODO: consider using here some light-weight functions instead of pseudo-real FirMemberFunctionImpl
@@ -120,7 +119,7 @@ object FirFakeOverrideGenerator {
newReturnType: ConeKotlinType? = null,
newModality: Modality? = null,
newVisibility: Visibility? = null,
deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation? = null,
deferredReturnTypeCalculation: DeferredCallableCopyReturnType? = null,
newSource: KtSourceElement? = derivedClassLookupTag?.toSymbol(session)?.source ?: baseFunction.source,
): FirSimpleFunction = buildSimpleFunction {
source = newSource
@@ -155,7 +154,7 @@ object FirFakeOverrideGenerator {
newContextReceiverTypes: List<ConeKotlinType?>?,
newTypeParameters: List<FirTypeParameterRef>?,
isExpect: Boolean,
deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation?,
deferredReturnTypeCalculation: DeferredCallableCopyReturnType?,
newSource: KtSourceElement? = null,
): FirConstructor = buildConstructor {
// TODO: consider using here some light-weight functions instead of pseudo-real FirMemberFunctionImpl
@@ -209,7 +208,7 @@ object FirFakeOverrideGenerator {
newReceiverType: ConeKotlinType?,
newContextReceiverTypes: List<ConeKotlinType?>?,
newReturnType: ConeKotlinType?,
deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation?,
deferredReturnTypeCalculation: DeferredCallableCopyReturnType?,
symbolForOverride: FirFunctionSymbol<*>,
copyDefaultValues: Boolean = false,
): List<FirTypeParameterRef> {
@@ -241,7 +240,7 @@ object FirFakeOverrideGenerator {
)
val (copiedReturnType, newCallableCopySubstitutionForTypeUpdater) = when (possibleReturnType) {
is Maybe.Value -> possibleReturnType.value to null
else -> null to CallableCopySubstitution(substitutor, symbol)
else -> null to DeferredReturnTypeOfSubstitution(substitutor, symbol)
}
configureAnnotationsAndSignature(
baseFunction,
@@ -280,7 +279,7 @@ object FirFakeOverrideGenerator {
newReceiverType: ConeKotlinType?,
newContextReceiverTypes: List<ConeKotlinType?>?,
newReturnType: ConeKotlinType?,
deferredTypeCalculation: CallableCopyDeferredReturnTypeCalculation?,
deferredTypeCalculation: DeferredCallableCopyReturnType?,
origin: FirDeclarationOrigin,
copyDefaultValues: Boolean = false,
) {
@@ -290,12 +289,12 @@ object FirFakeOverrideGenerator {
@Suppress("NAME_SHADOWING")
val deferredTypeCalculation = deferredTypeCalculation
?: runIf(baseFunction.returnTypeRef is FirImplicitTypeRef) {
CallableCopySubstitution(ConeSubstitutor.Empty, baseFunction.symbol)
DeferredReturnTypeOfSubstitution(ConeSubstitutor.Empty, baseFunction.symbol)
}
if (deferredTypeCalculation != null) {
returnTypeRef = FirImplicitTypeRefImplWithoutSource
attributes.callableCopyDeferredTypeCalculation = deferredTypeCalculation
attributes.deferredCallableCopyReturnType = deferredTypeCalculation
} else {
returnTypeRef = baseFunction.returnTypeRef.withReplacedReturnType(newReturnType)
}
@@ -366,7 +365,7 @@ object FirFakeOverrideGenerator {
newReturnType: ConeKotlinType? = null,
newTypeParameters: List<FirTypeParameter>? = null,
isExpect: Boolean = baseProperty.isExpect,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution? = null,
callableCopySubstitutionForTypeUpdater: DeferredCallableCopyReturnType? = null,
): FirPropertySymbol {
createCopyForFirProperty(
symbolForSubstitutionOverride, baseProperty, derivedClassLookupTag, session, origin,
@@ -400,7 +399,7 @@ object FirFakeOverrideGenerator {
newReturnType: ConeKotlinType? = null,
newModality: Modality? = null,
newVisibility: Visibility? = null,
deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation? = null,
deferredReturnTypeCalculation: DeferredCallableCopyReturnType? = null,
newSource: KtSourceElement? = derivedClassLookupTag?.toSymbol(session)?.source ?: baseProperty.source,
): FirProperty = buildProperty {
source = newSource
@@ -547,7 +546,7 @@ object FirFakeOverrideGenerator {
newReturnType: ConeKotlinType? = null,
newModality: Modality? = null,
newVisibility: Visibility? = null,
deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation? = null,
deferredReturnTypeCalculation: DeferredCallableCopyReturnType? = null,
): FirField = buildField {
source = baseField.source
moduleData = session.nullableModuleData ?: baseField.moduleData
@@ -576,7 +575,7 @@ object FirFakeOverrideGenerator {
newReceiverType: ConeKotlinType?,
newContextReceiverTypes: List<ConeKotlinType?>?,
newReturnType: ConeKotlinType?,
deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation?,
deferredReturnTypeCalculation: DeferredCallableCopyReturnType?,
): List<FirTypeParameter> {
return when {
baseProperty.typeParameters.isEmpty() -> {
@@ -594,7 +593,7 @@ object FirFakeOverrideGenerator {
)
val (copiedReturnType, newCallableCopySubstitutionForTypeUpdater) = when (possibleReturnType) {
is Maybe.Value -> possibleReturnType.value to null
else -> null to CallableCopySubstitution(substitutor, baseProperty.symbol)
else -> null to DeferredReturnTypeOfSubstitution(substitutor, baseProperty.symbol)
}
configureAnnotationsAndSignature(
baseProperty, copiedReceiverType, copiedContextReceiverTypes,
@@ -648,7 +647,7 @@ object FirFakeOverrideGenerator {
newReceiverType: ConeKotlinType?,
newContextReceiverTypes: List<ConeKotlinType?>?,
newReturnType: ConeKotlinType?,
deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation?,
deferredReturnTypeCalculation: DeferredCallableCopyReturnType?,
updateReceiver: Boolean = true,
) {
checkStatusIsResolved(baseVariable)
@@ -657,12 +656,12 @@ object FirFakeOverrideGenerator {
@Suppress("NAME_SHADOWING")
val deferredReturnTypeCalculation = deferredReturnTypeCalculation
?: runIf(baseVariable.returnTypeRef is FirImplicitTypeRef) {
CallableCopySubstitution(ConeSubstitutor.Empty, baseVariable.symbol)
DeferredReturnTypeOfSubstitution(ConeSubstitutor.Empty, baseVariable.symbol)
}
if (deferredReturnTypeCalculation != null) {
returnTypeRef = FirImplicitTypeRefImplWithoutSource
attributes.callableCopyDeferredTypeCalculation = deferredReturnTypeCalculation
attributes.deferredCallableCopyReturnType = deferredReturnTypeCalculation
} else {
returnTypeRef = baseVariable.returnTypeRef.withReplacedReturnType(newReturnType)
}
@@ -427,7 +427,7 @@ class FirTypeIntersectionScopeContext(
overrides: Collection<FirCallableSymbol<*>>,
containsMultipleNonSubsumed: Boolean,
createIntersectionOverrideSymbol: (CallableId, Collection<FirCallableSymbol<*>>, Boolean) -> S,
createCopy: (S, F, deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation?, returnType: ConeKotlinType?) -> F
createCopy: (S, F, deferredReturnTypeCalculation: DeferredCallableCopyReturnType?, returnType: ConeKotlinType?) -> F
): S {
val key = mostSpecific.first() as S
val keyFir = key.fir
@@ -444,32 +444,46 @@ class FirTypeIntersectionScopeContext(
return newSymbol
}
private fun deferredReturnTypeCalculationOrNull(mostSpecific: Collection<FirCallableSymbol<*>>): CallableCopyIntersection? {
private fun deferredReturnTypeCalculationOrNull(mostSpecific: Collection<FirCallableSymbol<*>>): DeferredReturnTypeOfIntersection? {
return runIf(mostSpecific.any { it.fir.returnTypeRef is FirImplicitTypeRef }) {
CallableCopyIntersection(mostSpecific, session)
DeferredReturnTypeOfIntersection(mostSpecific, session)
}
}
private fun intersectReturnTypes(overrides: Collection<FirCallableSymbol<*>>): ConeKotlinType? {
return intersectReturnTypes(overrides, session) { returnTypeRef.coneType }
}
}
companion object {
inline fun intersectReturnTypes(overrides: Collection<FirCallableSymbol<*>>, session: FirSession, getReturnType: FirCallableDeclaration.() -> ConeKotlinType?): ConeKotlinType? {
val key = overrides.first()
// Remap type parameters to the first declaration's:
// (fun <A, B> foo(): B) & (fun <C, D> foo(): D?) -> (fun <A, B> foo(): B & B?)
val substituted = overrides.mapNotNull {
val returnType = it.fir.getReturnType() ?: return@mapNotNull null
if (it == key) return@mapNotNull returnType
val substitutor = buildSubstitutorForOverridesCheck(it.fir, key.fir, session) ?: return@mapNotNull null
returnType.let(substitutor::substituteOrSelf)
}
return if (substituted.isNotEmpty()) session.typeContext.intersectTypes(substituted) else null
private class DeferredReturnTypeOfIntersection(
private val mostSpecific: Collection<FirCallableSymbol<*>>,
private val session: FirSession,
) : DeferredCallableCopyReturnType() {
override fun computeReturnType(calc: CallableCopyTypeCalculator): ConeKotlinType? {
return intersectReturnTypes(mostSpecific, session) {
calc.computeReturnTypeOrNull(this)
}
}
override fun toString(): String {
return "CallableCopyIntersection(mostSpecific=$mostSpecific)"
}
}
private fun intersectReturnTypes(overrides: Collection<FirCallableSymbol<*>>, session: FirSession, getReturnType: FirCallableDeclaration.() -> ConeKotlinType?): ConeKotlinType? {
val key = overrides.first()
// Remap type parameters to the first declaration's:
// (fun <A, B> foo(): B) & (fun <C, D> foo(): D?) -> (fun <A, B> foo(): B & B?)
val substituted = overrides.mapNotNull {
val returnType = it.fir.getReturnType() ?: return@mapNotNull null
if (it == key) return@mapNotNull returnType
val substitutor = buildSubstitutorForOverridesCheck(it.fir, key.fir, session) ?: return@mapNotNull null
returnType.let(substitutor::substituteOrSelf)
}
return if (substituted.isNotEmpty()) session.typeContext.intersectTypes(substituted) else null
}
private fun <D : FirCallableSymbol<*>> D.withScope(baseScope: FirTypeScope) = MemberWithBaseScope(this, baseScope)
typealias FirIntersectionOverrideCache =
@@ -346,8 +346,8 @@ open class ReturnTypeCalculatorWithJump(
return newReturnTypeRef
}
private inner class CallableCopyTypeCalculatorWithJump : CallableCopyTypeCalculator.AbstractCallableCopyTypeCalculator() {
override fun FirCallableDeclaration.getResolvedTypeRef(): FirResolvedTypeRef? {
private inner class CallableCopyTypeCalculatorWithJump : CallableCopyTypeCalculator.DeferredCallableCopyTypeCalculator() {
override fun FirCallableDeclaration.getResolvedTypeRef(): FirResolvedTypeRef {
return this@ReturnTypeCalculatorWithJump.computeReturnTypeRef(this)
}
}