[FIR] Defer computation of intersection override return type if overridden has implicit return type

#KT-59921
This commit is contained in:
Kirill Rakhman
2024-01-18 16:54:28 +01:00
committed by Space Team
parent 9ec0210c04
commit 1238f081b7
47 changed files with 252 additions and 200 deletions
@@ -298,7 +298,7 @@ class JvmMappedScope(
newTypeParameters = null,
newContextReceiverTypes = emptyList(),
isExpect = false,
callableCopySubstitutionForTypeUpdater = null,
deferredReturnTypeCalculation = null,
newSource = oldConstructor.source,
)
return newSymbol
@@ -5,6 +5,7 @@
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
@@ -12,7 +13,10 @@ 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.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
@@ -27,28 +31,46 @@ abstract class CallableCopyTypeCalculator {
abstract class AbstractCallableCopyTypeCalculator : CallableCopyTypeCalculator() {
override fun computeReturnType(declaration: FirCallableDeclaration): FirResolvedTypeRef? {
val callableCopySubstitutionForTypeUpdater = declaration.attributes.callableCopySubstitutionForTypeUpdater
val callableCopyDeferredTypeCalculation = declaration.attributes.callableCopyDeferredTypeCalculation
?: return declaration.getResolvedTypeRef()
// TODO: drop synchronized in KT-60385
synchronized(callableCopySubstitutionForTypeUpdater) {
if (declaration.attributes.callableCopySubstitutionForTypeUpdater == null) {
synchronized(callableCopyDeferredTypeCalculation) {
if (declaration.attributes.callableCopyDeferredTypeCalculation == null) {
return declaration.returnTypeRef as FirResolvedTypeRef
}
val (substitutor, baseSymbol) = callableCopySubstitutionForTypeUpdater
val baseDeclaration = baseSymbol.fir as FirCallableDeclaration
val baseReturnType = computeReturnType(baseDeclaration)?.type ?: return null
val coneType = substitutor.substituteOrSelf(baseReturnType)
val returnType = declaration.returnTypeRef.resolvedTypeFromPrototype(coneType)
declaration.replaceReturnTypeRef(returnType)
val returnType = when (callableCopyDeferredTypeCalculation) {
is CallableCopySubstitution -> computeSubstitution(callableCopyDeferredTypeCalculation)
is CallableCopyIntersection -> computeIntersection(callableCopyDeferredTypeCalculation)
} ?: return null
val returnTypeRef = declaration.returnTypeRef.resolvedTypeFromPrototype(returnType)
declaration.replaceReturnTypeRef(returnTypeRef)
if (declaration is FirProperty) {
declaration.getter?.replaceReturnTypeRef(returnType)
declaration.setter?.valueParameters?.firstOrNull()?.replaceReturnTypeRef(returnType)
declaration.getter?.replaceReturnTypeRef(returnTypeRef)
declaration.setter?.valueParameters?.firstOrNull()?.replaceReturnTypeRef(returnTypeRef)
}
declaration.attributes.callableCopySubstitutionForTypeUpdater = null
return returnType
declaration.attributes.callableCopyDeferredTypeCalculation = 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
}
}
@@ -65,13 +87,24 @@ abstract class CallableCopyTypeCalculator {
// ---------------------------------------------------------------------------------------------------------------------------------------
private object CallableCopySubstitutionKey : FirDeclarationDataKey()
private object CallableCopyDeferredReturnTypeCalculationKey : FirDeclarationDataKey()
var FirDeclarationAttributes.callableCopySubstitutionForTypeUpdater: CallableCopySubstitution? by FirDeclarationDataRegistry.attributesAccessor(
CallableCopySubstitutionKey
var FirDeclarationAttributes.callableCopyDeferredTypeCalculation: CallableCopyDeferredReturnTypeCalculation? by FirDeclarationDataRegistry.attributesAccessor(
CallableCopyDeferredReturnTypeCalculationKey
)
sealed class CallableCopyDeferredReturnTypeCalculation
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)"
}
}
@@ -20,8 +20,9 @@ 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.callableCopySubstitutionForTypeUpdater
import org.jetbrains.kotlin.fir.scopes.callableCopyDeferredTypeCalculation
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
@@ -98,7 +99,7 @@ object FirFakeOverrideGenerator {
newReceiverType,
newContextReceiverTypes,
newReturnType,
callableCopySubstitutionForTypeUpdater = callableCopySubstitutionForTypeUpdater
deferredReturnTypeCalculation = callableCopySubstitutionForTypeUpdater
).apply {
originalForSubstitutionOverrideAttr = baseFunction
}
@@ -119,7 +120,7 @@ object FirFakeOverrideGenerator {
newReturnType: ConeKotlinType? = null,
newModality: Modality? = null,
newVisibility: Visibility? = null,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution? = null,
deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation? = null,
newSource: KtSourceElement? = null,
): FirSimpleFunction = buildSimpleFunction {
source = newSource ?: derivedClassLookupTag?.toSymbol(session)?.source ?: baseFunction.source
@@ -134,7 +135,7 @@ object FirFakeOverrideGenerator {
attributes = baseFunction.attributes.copy()
typeParameters += configureAnnotationsTypeParametersAndSignature(
session, baseFunction, newParameterTypes, newTypeParameters,
newReceiverType, newContextReceiverTypes, newReturnType, callableCopySubstitutionForTypeUpdater, newSymbol
newReceiverType, newContextReceiverTypes, newReturnType, deferredReturnTypeCalculation, newSymbol
).filterIsInstance<FirTypeParameter>()
deprecationsProvider = baseFunction.deprecationsProvider
}.apply {
@@ -153,7 +154,7 @@ object FirFakeOverrideGenerator {
newContextReceiverTypes: List<ConeKotlinType?>?,
newTypeParameters: List<FirTypeParameterRef>?,
isExpect: Boolean,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution?,
deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation?,
newSource: KtSourceElement? = null,
): FirConstructor = buildConstructor {
// TODO: consider using here some light-weight functions instead of pseudo-real FirMemberFunctionImpl
@@ -179,7 +180,7 @@ object FirFakeOverrideGenerator {
newReceiverType = null,
newContextReceiverTypes,
newReturnType,
callableCopySubstitutionForTypeUpdater,
deferredReturnTypeCalculation,
fakeOverrideSymbol
)
@@ -201,7 +202,7 @@ object FirFakeOverrideGenerator {
newReceiverType: ConeKotlinType?,
newContextReceiverTypes: List<ConeKotlinType?>?,
newReturnType: ConeKotlinType?,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution?,
deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation?,
symbolForOverride: FirFunctionSymbol<*>,
): List<FirTypeParameterRef> {
return when {
@@ -213,7 +214,7 @@ object FirFakeOverrideGenerator {
newReceiverType,
newContextReceiverTypes,
newReturnType,
callableCopySubstitutionForTypeUpdater,
deferredReturnTypeCalculation,
origin,
)
emptyList()
@@ -253,7 +254,7 @@ object FirFakeOverrideGenerator {
newReceiverType,
newContextReceiverTypes,
newReturnType,
callableCopySubstitutionForTypeUpdater,
deferredReturnTypeCalculation,
origin,
)
newTypeParameters
@@ -268,21 +269,21 @@ object FirFakeOverrideGenerator {
newReceiverType: ConeKotlinType?,
newContextReceiverTypes: List<ConeKotlinType?>?,
newReturnType: ConeKotlinType?,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution?,
deferredTypeCalculation: CallableCopyDeferredReturnTypeCalculation?,
origin: FirDeclarationOrigin,
) {
checkStatusIsResolved(baseFunction)
annotations += baseFunction.annotations
@Suppress("NAME_SHADOWING")
val callableCopySubstitutionForTypeUpdater = callableCopySubstitutionForTypeUpdater
val deferredTypeCalculation = deferredTypeCalculation
?: runIf(baseFunction.returnTypeRef is FirImplicitTypeRef) {
CallableCopySubstitution(ConeSubstitutor.Empty, baseFunction.symbol)
}
if (callableCopySubstitutionForTypeUpdater != null) {
if (deferredTypeCalculation != null) {
returnTypeRef = FirImplicitTypeRefImplWithoutSource
attributes.callableCopySubstitutionForTypeUpdater = callableCopySubstitutionForTypeUpdater
attributes.callableCopyDeferredTypeCalculation = deferredTypeCalculation
} else {
returnTypeRef = baseFunction.returnTypeRef.withReplacedReturnType(newReturnType)
}
@@ -354,7 +355,7 @@ object FirFakeOverrideGenerator {
createCopyForFirProperty(
symbolForSubstitutionOverride, baseProperty, derivedClassLookupTag, session, origin,
isExpect, newDispatchReceiverType, newTypeParameters, newReceiverType, newContextReceiverTypes, newReturnType,
callableCopySubstitutionForTypeUpdater = callableCopySubstitutionForTypeUpdater
deferredReturnTypeCalculation = callableCopySubstitutionForTypeUpdater
).apply {
originalForSubstitutionOverrideAttr = baseProperty
}
@@ -383,7 +384,7 @@ object FirFakeOverrideGenerator {
newReturnType: ConeKotlinType? = null,
newModality: Modality? = null,
newVisibility: Visibility? = null,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution? = null,
deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation? = null,
): FirProperty = buildProperty {
source = derivedClassLookupTag?.toSymbol(session)?.source ?: baseProperty.source
moduleData = session.nullableModuleData ?: baseProperty.moduleData
@@ -404,7 +405,7 @@ object FirFakeOverrideGenerator {
newReceiverType,
newContextReceiverTypes,
newReturnType,
callableCopySubstitutionForTypeUpdater
deferredReturnTypeCalculation
)
deprecationsProvider = baseProperty.deprecationsProvider
@@ -529,7 +530,7 @@ object FirFakeOverrideGenerator {
newReturnType: ConeKotlinType? = null,
newModality: Modality? = null,
newVisibility: Visibility? = null,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution? = null,
deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation? = null,
): FirField = buildField {
source = baseField.source
moduleData = session.nullableModuleData ?: baseField.moduleData
@@ -544,7 +545,7 @@ object FirFakeOverrideGenerator {
attributes = baseField.attributes.copy()
configureAnnotationsAndSignature(
baseField, newReceiverType, newContextReceiverTypes, newReturnType,
callableCopySubstitutionForTypeUpdater, updateReceiver = false
deferredReturnTypeCalculation, updateReceiver = false
)
deprecationsProvider = baseField.deprecationsProvider
}.apply {
@@ -558,12 +559,12 @@ object FirFakeOverrideGenerator {
newReceiverType: ConeKotlinType?,
newContextReceiverTypes: List<ConeKotlinType?>?,
newReturnType: ConeKotlinType?,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution?,
deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation?,
): List<FirTypeParameter> {
return when {
baseProperty.typeParameters.isEmpty() -> {
configureAnnotationsAndSignature(
baseProperty, newReceiverType, newContextReceiverTypes, newReturnType, callableCopySubstitutionForTypeUpdater
baseProperty, newReceiverType, newContextReceiverTypes, newReturnType, deferredReturnTypeCalculation
)
emptyList()
}
@@ -586,7 +587,7 @@ object FirFakeOverrideGenerator {
}
else -> {
configureAnnotationsAndSignature(
baseProperty, newReceiverType, newContextReceiverTypes, newReturnType, callableCopySubstitutionForTypeUpdater
baseProperty, newReceiverType, newContextReceiverTypes, newReturnType, deferredReturnTypeCalculation
)
newTypeParameters
}
@@ -630,21 +631,21 @@ object FirFakeOverrideGenerator {
newReceiverType: ConeKotlinType?,
newContextReceiverTypes: List<ConeKotlinType?>?,
newReturnType: ConeKotlinType?,
callableCopySubstitutionForTypeUpdater: CallableCopySubstitution?,
deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation?,
updateReceiver: Boolean = true,
) {
checkStatusIsResolved(baseVariable)
annotations += baseVariable.annotations
@Suppress("NAME_SHADOWING")
val callableCopySubstitutionForTypeUpdater = callableCopySubstitutionForTypeUpdater
val deferredReturnTypeCalculation = deferredReturnTypeCalculation
?: runIf(baseVariable.returnTypeRef is FirImplicitTypeRef) {
CallableCopySubstitution(ConeSubstitutor.Empty, baseVariable.symbol)
}
if (callableCopySubstitutionForTypeUpdater != null) {
if (deferredReturnTypeCalculation != null) {
returnTypeRef = FirImplicitTypeRefImplWithoutSource
attributes.callableCopySubstitutionForTypeUpdater = callableCopySubstitutionForTypeUpdater
attributes.callableCopyDeferredTypeCalculation = deferredReturnTypeCalculation
} else {
returnTypeRef = baseVariable.returnTypeRef.withReplacedReturnType(newReturnType)
}
@@ -347,6 +347,7 @@ class FirTypeIntersectionScopeContext(
keyFir.name
)
val newSymbol = FirIntersectionOverrideFunctionSymbol(callableId, overrides)
val deferredReturnTypeCalculation = deferredReturnTypeCalculationOrNull(mostSpecific)
FirFakeOverrideGenerator.createCopyForFirFunction(
newSymbol, keyFir, derivedClassLookupTag = null, session,
FirDeclarationOrigin.IntersectionOverride,
@@ -354,7 +355,8 @@ class FirTypeIntersectionScopeContext(
newModality = newModality,
newVisibility = newVisibility,
newDispatchReceiverType = dispatchReceiverType,
newReturnType = if (!forClassUseSiteScope) intersectReturnTypes(mostSpecific) else null,
deferredReturnTypeCalculation = deferredReturnTypeCalculation,
newReturnType = if (!forClassUseSiteScope && deferredReturnTypeCalculation == null) intersectReturnTypes(mostSpecific) else null,
).apply {
originalForIntersectionOverrideAttr = keyFir
}
@@ -371,7 +373,7 @@ class FirTypeIntersectionScopeContext(
mostSpecific,
overrides,
::FirIntersectionOverridePropertySymbol,
) { symbol, fir, returnType ->
) { symbol, fir, deferredReturnTypeCalculation, returnType ->
FirFakeOverrideGenerator.createCopyForFirProperty(
symbol, fir, derivedClassLookupTag = null, session,
FirDeclarationOrigin.IntersectionOverride,
@@ -379,6 +381,7 @@ class FirTypeIntersectionScopeContext(
newModality = newModality,
newVisibility = newVisibility,
newDispatchReceiverType = dispatchReceiverType,
deferredReturnTypeCalculation = deferredReturnTypeCalculation,
// If any of the properties are vars and the types are not equal, these declarations are conflicting
// anyway and their uses should result in an overload resolution error.
newReturnType = returnType
@@ -396,7 +399,7 @@ class FirTypeIntersectionScopeContext(
mostSpecific,
overrides,
::FirIntersectionOverrideFieldSymbol
) { symbol, fir, returnType ->
) { symbol, fir, deferredReturnTypeCalculation, returnType ->
FirFakeOverrideGenerator.createCopyForFirField(
symbol, fir, derivedClassLookupTag = null, session,
FirDeclarationOrigin.IntersectionOverride,
@@ -404,6 +407,7 @@ class FirTypeIntersectionScopeContext(
newModality = newModality,
newVisibility = newVisibility,
newDispatchReceiverType = dispatchReceiverType,
deferredReturnTypeCalculation = deferredReturnTypeCalculation,
// If any of the properties are vars and the types are not equal, these declarations are conflicting
// anyway and their uses should result in an overload resolution error.
newReturnType = returnType
@@ -415,32 +419,46 @@ class FirTypeIntersectionScopeContext(
mostSpecific: Collection<FirCallableSymbol<*>>,
overrides: Collection<FirCallableSymbol<*>>,
createIntersectionOverrideSymbol: (CallableId, Collection<FirCallableSymbol<*>>) -> S,
createCopy: (S, F, returnType: ConeKotlinType?) -> F
createCopy: (S, F, deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation?, returnType: ConeKotlinType?) -> F
): S {
val key = mostSpecific.first() as S
val keyFir = key.fir
val callableId = CallableId(dispatchReceiverType.classId ?: keyFir.dispatchReceiverClassLookupTagOrNull()?.classId!!, keyFir.name)
val newSymbol = createIntersectionOverrideSymbol(callableId, overrides)
val newReturnType = runIf(!forClassUseSiteScope && mostSpecific.none { (it as FirVariableSymbol<*>).fir.isVar }) {
intersectReturnTypes(mostSpecific)
}
createCopy(newSymbol, keyFir, newReturnType).apply {
val deferredReturnTypeCalculation = deferredReturnTypeCalculationOrNull(mostSpecific)
val newReturnType =
runIf(!forClassUseSiteScope && mostSpecific.none { (it as FirVariableSymbol<*>).fir.isVar } && deferredReturnTypeCalculation == null) {
intersectReturnTypes(mostSpecific)
}
createCopy(newSymbol, keyFir, deferredReturnTypeCalculation, newReturnType).apply {
originalForIntersectionOverrideAttr = keyFir
}
return newSymbol
}
private fun intersectReturnTypes(overrides: Collection<FirCallableSymbol<*>>): 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.returnTypeRef.coneTypeSafe<ConeKotlinType>()
if (it == key) return@mapNotNull returnType
val substitutor = buildSubstitutorForOverridesCheck(it.fir, key.fir, session) ?: return@mapNotNull null
returnType?.let(substitutor::substituteOrSelf)
private fun deferredReturnTypeCalculationOrNull(mostSpecific: Collection<FirCallableSymbol<*>>): CallableCopyIntersection? {
return runIf(mostSpecific.any { it.fir.returnTypeRef is FirImplicitTypeRef }) {
CallableCopyIntersection(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
}
return if (substituted.isNotEmpty()) session.typeContext.intersectTypes(substituted) else null
}
}