[FIR] Introduce ConeFunctionalTypeKind as a replacement of FunctionClassKind

This is needed to provide an ability to extend different kinds of
  functional types

Also, cleanup and rename utilities related to functional types to avoid
  possible confusions
This commit is contained in:
Dmitriy Novozhilov
2023-01-18 14:12:11 +02:00
committed by Space Team
parent c98c8d3682
commit c86495dcae
40 changed files with 583 additions and 365 deletions
@@ -33,7 +33,14 @@ internal class KtFirTypeInfoProvider(
override fun getFunctionClassKind(type: KtType): FunctionClassKind? { override fun getFunctionClassKind(type: KtType): FunctionClassKind? {
val coneType = (type as KtFirType).coneType val coneType = (type as KtFirType).coneType
return coneType.functionClassKind(analysisSession.useSiteSession) return when (coneType.functionalTypeKind(analysisSession.useSiteSession)) {
ConeFunctionalTypeKind.Function -> FunctionClassKind.Function
ConeFunctionalTypeKind.KFunction -> FunctionClassKind.KFunction
ConeFunctionalTypeKind.SuspendFunction -> FunctionClassKind.SuspendFunction
ConeFunctionalTypeKind.KSuspendFunction -> FunctionClassKind.KSuspendFunction
null -> null
else -> null // TODO: consider handling of custom functional type kinds from plugins
}
} }
override fun canBeNull(type: KtType): Boolean = (type as KtFirType).coneType.canBeNull override fun canBeNull(type: KtType): Boolean = (type as KtFirType).coneType.canBeNull
@@ -49,9 +49,10 @@ internal class KtFirFunctionalType(
override val nullability: KtTypeNullability get() = withValidityAssertion { coneType.nullability.asKtNullability() } override val nullability: KtTypeNullability get() = withValidityAssertion { coneType.nullability.asKtNullability() }
override val isSuspend: Boolean get() = withValidityAssertion { coneType.isSuspendOrKSuspendFunctionType(builder.rootSession) } override val isSuspend: Boolean get() = withValidityAssertion { coneType.isSuspendFunctionType(builder.rootSession) }
override val isReflectType: Boolean get() = withValidityAssertion { coneType.isKFunctionType(builder.rootSession) } override val isReflectType: Boolean
get() = withValidityAssertion { coneType.functionalTypeKind(builder.rootSession)?.isReflectType == true }
override val arity: Int override val arity: Int
get() = withValidityAssertion { get() = withValidityAssertion {
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.isAbstract
import org.jetbrains.kotlin.fir.declarations.utils.isInline import org.jetbrains.kotlin.fir.declarations.utils.isInline
import org.jetbrains.kotlin.fir.declarations.utils.isSuspend import org.jetbrains.kotlin.fir.declarations.utils.isSuspend
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isSuspendOrKSuspendFunctionType import org.jetbrains.kotlin.fir.types.isSuspendFunctionType
import org.jetbrains.kotlin.name.JvmNames.SYNCHRONIZED_ANNOTATION_CLASS_ID import org.jetbrains.kotlin.name.JvmNames.SYNCHRONIZED_ANNOTATION_CLASS_ID
object FirSynchronizedAnnotationChecker : FirFunctionChecker() { object FirSynchronizedAnnotationChecker : FirFunctionChecker() {
@@ -33,7 +33,7 @@ object FirSynchronizedAnnotationChecker : FirFunctionChecker() {
return return
} }
if (declaration.isSuspend || if (declaration.isSuspend ||
(declaration as? FirAnonymousFunction)?.typeRef?.coneType?.isSuspendOrKSuspendFunctionType(session) == true (declaration as? FirAnonymousFunction)?.typeRef?.coneType?.isSuspendFunctionType(session) == true
) { ) {
reporter.reportOn(annotation.source, FirJvmErrors.SYNCHRONIZED_ON_SUSPEND, context) reporter.reportOn(annotation.source, FirJvmErrors.SYNCHRONIZED_ON_SUSPEND, context)
return return
@@ -278,7 +278,7 @@ object FirCallsEffectAnalyzer : FirControlFlowChecker() {
} }
private fun FirTypeRef?.isFunctionalTypeRef(session: FirSession): Boolean { private fun FirTypeRef?.isFunctionalTypeRef(session: FirSession): Boolean {
return this?.coneType?.isBuiltinFunctionalType(session) == true return this?.coneType?.isSomeFunctionalType(session) == true
} }
private fun FirContractDescription?.getParameterCallsEffectDeclaration(index: Int): ConeCallsEffectDeclaration? { private fun FirContractDescription?.getParameterCallsEffectDeclaration(index: Int): ConeCallsEffectDeclaration? {
@@ -144,7 +144,7 @@ internal fun checkConstantArguments(
} }
expression is FirQualifiedAccessExpression -> { expression is FirQualifiedAccessExpression -> {
val expressionType = expression.typeRef.coneType val expressionType = expression.typeRef.coneType
if (expressionType.isKFunctionType(session) || expressionType.isKProperty(session) || expressionType.isKMutableProperty(session)) { if (expressionType.isReflectFunctionalType(session) || expressionType.isKProperty(session) || expressionType.isKMutableProperty(session)) {
return checkConstantArguments(expression.dispatchReceiver, session) return checkConstantArguments(expression.dispatchReceiver, session)
} }
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.builtins.StandardNames.BACKING_FIELD import org.jetbrains.kotlin.builtins.StandardNames.BACKING_FIELD
import org.jetbrains.kotlin.builtins.functions.isSuspendType
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.EffectiveVisibility import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
@@ -61,7 +62,7 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
val inalienableParameters = function.valueParameters.filter { val inalienableParameters = function.valueParameters.filter {
if (it.isNoinline) return@filter false if (it.isNoinline) return@filter false
val type = it.returnTypeRef.coneType val type = it.returnTypeRef.coneType
!type.isMarkedNullable && type.isFunctionalType(context.session) { kind -> !kind.isReflectType } !type.isMarkedNullable && type.isNonReflectFunctionalType(context.session)
}.map { it.symbol } }.map { it.symbol }
val visitor = inlineVisitor( val visitor = inlineVisitor(
@@ -233,7 +234,7 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
// TODO: receivers are currently not inline (KT-5837) // TODO: receivers are currently not inline (KT-5837)
// if (targetSymbol.isInline) return true // if (targetSymbol.isInline) return true
return targetSymbol.name == OperatorNameConventions.INVOKE && return targetSymbol.name == OperatorNameConventions.INVOKE &&
targetSymbol.dispatchReceiverType?.isBuiltinFunctionalType(session) == true targetSymbol.dispatchReceiverType?.isSomeFunctionalType(session) == true
} }
private fun checkQualifiedAccess( private fun checkQualifiedAccess(
@@ -404,8 +405,9 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
) { ) {
for (param in function.valueParameters) { for (param in function.valueParameters) {
val coneType = param.returnTypeRef.coneType val coneType = param.returnTypeRef.coneType
val isFunctionalType = coneType.isFunctionalType(context.session) val functionalKind = coneType.functionalTypeKind(context.session)
val isSuspendFunctionalType = coneType.isSuspendOrKSuspendFunctionType(context.session) val isFunctionalType = functionalKind != null
val isSuspendFunctionalType = functionalKind?.isSuspendType == true
val defaultValue = param.defaultValue val defaultValue = param.defaultValue
if (!(isFunctionalType || isSuspendFunctionalType) && (param.isNoinline || param.isCrossinline)) { if (!(isFunctionalType || isSuspendFunctionalType) && (param.isNoinline || param.isCrossinline)) {
@@ -488,7 +490,7 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
function.valueParameters.any { param -> function.valueParameters.any { param ->
val type = param.returnTypeRef.coneType val type = param.returnTypeRef.coneType
!param.isNoinline && !type.isNullable !param.isNoinline && !type.isNullable
&& (type.isFunctionalType(session) || type.isSuspendOrKSuspendFunctionType(session)) && (type.isSimpleFunctionType(session) || type.isSuspendFunctionType(session))
} }
if (hasInlinableParameters) return if (hasInlinableParameters) return
if (function.isInlineOnly(session)) return if (function.isInlineOnly(session)) return
@@ -130,7 +130,7 @@ object FirSuspendCallChecker : FirQualifiedAccessExpressionChecker() {
private fun findEnclosingSuspendFunction(context: CheckerContext): FirFunction? { private fun findEnclosingSuspendFunction(context: CheckerContext): FirFunction? {
return context.containingDeclarations.lastOrNull { return context.containingDeclarations.lastOrNull {
when (it) { when (it) {
is FirAnonymousFunction -> it.typeRef.coneType.isSuspendOrKSuspendFunctionType(context.session) is FirAnonymousFunction -> it.typeRef.coneType.isSuspendFunctionType(context.session)
is FirSimpleFunction -> it.isSuspend is FirSimpleFunction -> it.isSuspend
else -> false else -> false
} }
@@ -218,7 +218,7 @@ object FirSuspendCallChecker : FirQualifiedAccessExpressionChecker() {
calledDeclarationSymbol: FirCallableSymbol<*> calledDeclarationSymbol: FirCallableSymbol<*>
): Triple<FirExpression?, FirExpression?, ConeKotlinType?> { ): Triple<FirExpression?, FirExpression?, ConeKotlinType?> {
if (this is FirImplicitInvokeCall && if (this is FirImplicitInvokeCall &&
dispatchReceiver != FirNoReceiverExpression && dispatchReceiver.typeRef.coneType.isSuspendOrKSuspendFunctionType(session) dispatchReceiver != FirNoReceiverExpression && dispatchReceiver.typeRef.coneType.isSuspendFunctionType(session)
) { ) {
val variableForInvoke = dispatchReceiver val variableForInvoke = dispatchReceiver
val variableForInvokeType = variableForInvoke.typeRef.coneType val variableForInvokeType = variableForInvoke.typeRef.coneType
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isFunctionalType import org.jetbrains.kotlin.fir.types.isSimpleFunctionType
object UnusedChecker : AbstractFirPropertyInitializationChecker() { object UnusedChecker : AbstractFirPropertyInitializationChecker() {
override fun analyze(data: PropertyInitializationInfoData, reporter: DiagnosticReporter, context: CheckerContext) { override fun analyze(data: PropertyInitializationInfoData, reporter: DiagnosticReporter, context: CheckerContext) {
@@ -278,7 +278,7 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
val reference = node.fir.calleeReference.resolved ?: return dataForNode val reference = node.fir.calleeReference.resolved ?: return dataForNode
val functionSymbol = reference.resolvedSymbol as? FirFunctionSymbol<*> ?: return dataForNode val functionSymbol = reference.resolvedSymbol as? FirFunctionSymbol<*> ?: return dataForNode
val symbol = if (functionSymbol.callableId.callableName.identifier == "invoke") { val symbol = if (functionSymbol.callableId.callableName.identifier == "invoke") {
localProperties.find { it.name == reference.name && it.resolvedReturnTypeRef.coneType.isFunctionalType(session) } localProperties.find { it.name == reference.name && it.resolvedReturnTypeRef.coneType.isSimpleFunctionType(session) }
} else null } else null
symbol ?: return dataForNode symbol ?: return dataForNode
@@ -7,15 +7,15 @@ package org.jetbrains.kotlin.fir.analysis.checkers.type
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.getAllowedAnnotationTargets import org.jetbrains.kotlin.fir.analysis.checkers.getAllowedAnnotationTargets
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.toAnnotationClassId import org.jetbrains.kotlin.fir.declarations.toAnnotationClassId
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.isBuiltinFunctionalType import org.jetbrains.kotlin.fir.types.isSomeFunctionalType
import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.name.StandardClassIds
object FirTypeAnnotationChecker : FirTypeRefChecker() { object FirTypeAnnotationChecker : FirTypeRefChecker() {
@@ -33,7 +33,7 @@ object FirTypeAnnotationChecker : FirTypeRefChecker() {
} }
} }
if (annotation.toAnnotationClassId(context.session) == StandardClassIds.Annotations.ExtensionFunctionType) { if (annotation.toAnnotationClassId(context.session) == StandardClassIds.Annotations.ExtensionFunctionType) {
if (!typeRef.type.isBuiltinFunctionalType(context.session)) { if (!typeRef.type.isSomeFunctionalType(context.session)) {
if (context.languageVersionSettings.supportsFeature(LanguageFeature.ForbidExtensionFunctionTypeOnNonFunctionTypes)) { if (context.languageVersionSettings.supportsFeature(LanguageFeature.ForbidExtensionFunctionTypeOnNonFunctionTypes)) {
reporter.reportOn(annotation.source, FirErrors.WRONG_EXTENSION_FUNCTION_TYPE, context) reporter.reportOn(annotation.source, FirErrors.WRONG_EXTENSION_FUNCTION_TYPE, context)
} else { } else {
@@ -5,21 +5,20 @@
package org.jetbrains.kotlin.fir.renderer package org.jetbrains.kotlin.fir.renderer
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
open class ConeTypeRenderer() { open class ConeTypeRenderer {
lateinit var builder: StringBuilder lateinit var builder: StringBuilder
lateinit var idRenderer: ConeIdRenderer lateinit var idRenderer: ConeIdRenderer
open fun renderAsPossibleFunctionType( open fun renderAsPossibleFunctionType(
type: ConeKotlinType, renderType: ConeTypeProjection.() -> Unit = { render() } type: ConeKotlinType,
functionalClassKindExtractor: (ConeKotlinType) -> ConeFunctionalTypeKind?,
renderType: ConeTypeProjection.() -> Unit = { render() }
) { ) {
val kind = type.functionTypeKind val kind = functionalClassKindExtractor(type)
if (!kind.withPrettyRender()) { if (kind?.isReflectType != false) {
type.renderType() type.renderType()
return return
} }
@@ -27,8 +26,9 @@ open class ConeTypeRenderer() {
if (type.isMarkedNullable) { if (type.isMarkedNullable) {
builder.append("(") builder.append("(")
} }
if (kind == FunctionClassKind.SuspendFunction) { kind.prefixForTypeRender?.let {
builder.append("suspend ") builder.append(it)
builder.append(" ")
} }
val typeArguments = type.typeArguments val typeArguments = type.typeArguments
val isExtension = type.isExtensionFunctionType val isExtension = type.isExtensionFunctionType
@@ -57,14 +57,6 @@ open class ConeTypeRenderer() {
} }
} }
@OptIn(ExperimentalContracts::class)
private fun FunctionClassKind?.withPrettyRender(): Boolean {
contract {
returns(true) implies (this@withPrettyRender != null)
}
return this != null && this != FunctionClassKind.KSuspendFunction && this != FunctionClassKind.KFunction
}
fun render(type: ConeKotlinType) { fun render(type: ConeKotlinType) {
if (type !is ConeFlexibleType && type !is ConeDefinitelyNotNullType) { if (type !is ConeFlexibleType && type !is ConeDefinitelyNotNullType) {
// We don't render attributes for flexible/definitely not null types here, // We don't render attributes for flexible/definitely not null types here,
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.renderer package org.jetbrains.kotlin.fir.renderer
import org.jetbrains.kotlin.fir.types.ConeFunctionalTypeKind
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeProjection import org.jetbrains.kotlin.fir.types.ConeTypeProjection
@@ -16,9 +17,13 @@ class ConeTypeRendererForDebugging() : ConeTypeRenderer() {
idRenderer.builder = builder idRenderer.builder = builder
} }
override fun renderAsPossibleFunctionType(type: ConeKotlinType, renderType: ConeTypeProjection.() -> Unit) { override fun renderAsPossibleFunctionType(
type: ConeKotlinType,
functionalClassKindExtractor: (ConeKotlinType) -> ConeFunctionalTypeKind?,
renderType: ConeTypeProjection.() -> Unit
) {
builder.append("R|") builder.append("R|")
super.renderAsPossibleFunctionType(type, renderType) super.renderAsPossibleFunctionType(type, functionalClassKindExtractor, renderType)
builder.append("|") builder.append("|")
} }
} }
@@ -0,0 +1,128 @@
/*
* 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.
*/
package org.jetbrains.kotlin.fir.types
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
/**
* [ConeFunctionalTypeKind] describes a family of various similar functional types (like kotlin.FunctionN)
* All types in the same family have corresponding shape of classId: [packageFqName].[classNamePrefix]N,
* where `N` is an arity of function
*
* Functional type kind may be either reflect type or non-reflect type
* Each non-reflect kind should have corresponding reflect kind and vice-versa (like `kotlin.FunctionN` and `kotlin.reflection.KFunctionN`)
*
* Classes for such functional types are synthetic and generated by compiler itself
* [annotationOnInvokeClassId] is a classId of annotation, which will be added to `invoke` function of generated
* functional interface. This argument is mandatory for non-standard functional kinds
*
* Assume we have `some.CustomFunctionN` and `some.KCustomFunctionN` families with [annotationOnInvokeClassId] = /some.Anno
* Classes for those kinds will look like this:
*
* interface CustomFunctionN<P1, P2, ..., PN, R> : kotlin.Function<R> {
* @some.Anno
* operator fun invoke(p1: P1, p2: P2, ..., pN: PN): R
* }
*
* interface KCustomFunctionN<P1, P2, ..., PN, R> : kotlin.reflect.KFunction<R>, some.CustomFunctionN<P1, P2, ..., PN, R> {
* @some.Anno
* override operator fun invoke(p1: P1, p2: P2, ..., pN: PN): R
* }
*
* Note that if you provide some new functional type kind it's your responsibility to handle all references to it in backend
* with [IrGenerationExtension] implementation
*/
abstract class ConeFunctionalTypeKind internal constructor(
val packageFqName: FqName,
val classNamePrefix: String,
val isReflectType: Boolean,
val annotationOnInvokeClassId: ClassId?
) {
/*
* This constructor is needed to enforce not nullable [annotationOnInvokeClassId] for
* functional kinds provided by compiler plugins
*/
constructor(
packageFqName: FqName,
classNamePrefix: String,
annotationOnInvokeClassId: ClassId,
isReflectType: Boolean
) : this(packageFqName, classNamePrefix, isReflectType, annotationOnInvokeClassId)
/*
* Specifies how corresponding type will be rendered
* E.g. if `prefixForTypeRender = @Some` and type is `some.CustomFunction2<Int, String, Double>`,
* then type will be rendered as `@Some (Int, String) -> Double`
*/
open val prefixForTypeRender: String?
get() = null
/**
* @return corresponding non-reflect kind for reflect kind
* @return [this] if [isReflectType] is false
*
* Should be overridden for reflect kinds
*/
open fun nonReflectKind(): ConeFunctionalTypeKind {
return if (isReflectType) error("Should be overridden explicitly") else this
}
/**
* @return corresponding reflect kind for non-reflect kind
* @return [this] if [isReflectType] is true
*
* Should be overridden for non reflect kinds
*/
open fun reflectKind(): ConeFunctionalTypeKind {
return if (isReflectType) this else error("Should be overridden explicitly")
}
fun numberedClassName(arity: Int): Name = Name.identifier("$classNamePrefix$arity")
// ------------------------------------------- Builtin functional kinds -------------------------------------------
object Function : ConeFunctionalTypeKind(
StandardNames.BUILT_INS_PACKAGE_FQ_NAME,
"Function",
isReflectType = false,
annotationOnInvokeClassId = null
) {
override fun reflectKind(): ConeFunctionalTypeKind = KFunction
}
object SuspendFunction : ConeFunctionalTypeKind(
StandardNames.COROUTINES_PACKAGE_FQ_NAME,
"SuspendFunction",
isReflectType = false,
annotationOnInvokeClassId = null
) {
override val prefixForTypeRender: String
get() = "suspend"
override fun reflectKind(): ConeFunctionalTypeKind = KSuspendFunction
}
object KFunction : ConeFunctionalTypeKind(
StandardNames.KOTLIN_REFLECT_FQ_NAME,
"KFunction",
isReflectType = true,
annotationOnInvokeClassId = null
) {
override fun nonReflectKind(): ConeFunctionalTypeKind = Function
}
object KSuspendFunction : ConeFunctionalTypeKind(
StandardNames.KOTLIN_REFLECT_FQ_NAME,
"KSuspendFunction",
isReflectType = true,
annotationOnInvokeClassId = null
) {
override fun nonReflectKind(): ConeFunctionalTypeKind = SuspendFunction
}
}
@@ -5,8 +5,10 @@
package org.jetbrains.kotlin.fir.types package org.jetbrains.kotlin.fir.types
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind import org.jetbrains.kotlin.fir.renderer.ConeIdRendererForDebugging
import org.jetbrains.kotlin.fir.renderer.* import org.jetbrains.kotlin.fir.renderer.ConeIdShortRenderer
import org.jetbrains.kotlin.fir.renderer.ConeTypeRendererForDebugging
import org.jetbrains.kotlin.fir.renderer.ConeTypeRendererWithJavaFlexibleTypes
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
@@ -111,14 +113,6 @@ fun ConeClassLikeType.replaceArgumentsWithStarProjections(): ConeClassLikeType {
return withArguments(newArguments) return withArguments(newArguments)
} }
val ConeKotlinType?.functionTypeKind: FunctionClassKind?
get() {
val classId = (this as? ConeClassLikeType)?.lookupTag?.classId ?: return null
return FunctionClassKind.getFunctionalClassKind(
classId.shortClassName.asString(), classId.packageFqName
)
}
fun ConeKotlinType.renderForDebugging(): String { fun ConeKotlinType.renderForDebugging(): String {
val builder = StringBuilder() val builder = StringBuilder()
ConeTypeRendererForDebugging(builder).render(this) ConeTypeRendererForDebugging(builder).render(this)
@@ -46,6 +46,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirIntersectionOverrideStorage
import org.jetbrains.kotlin.fir.scopes.impl.FirSubstitutionOverrideStorage import org.jetbrains.kotlin.fir.scopes.impl.FirSubstitutionOverrideStorage
import org.jetbrains.kotlin.fir.symbols.FirLazyDeclarationResolver import org.jetbrains.kotlin.fir.symbols.FirLazyDeclarationResolver
import org.jetbrains.kotlin.fir.types.FirCorrespondingSupertypesCache import org.jetbrains.kotlin.fir.types.FirCorrespondingSupertypesCache
import org.jetbrains.kotlin.fir.types.FirFunctionalTypeKindService
import org.jetbrains.kotlin.fir.types.TypeComponents import org.jetbrains.kotlin.fir.types.TypeComponents
import org.jetbrains.kotlin.incremental.components.EnumWhenTracker import org.jetbrains.kotlin.incremental.components.EnumWhenTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
@@ -71,6 +72,9 @@ fun FirSession.registerCommonComponents(languageVersionSettings: LanguageVersion
register(FirSamConstructorStorage::class, FirSamConstructorStorage(this)) register(FirSamConstructorStorage::class, FirSamConstructorStorage(this))
register(FirOverrideService::class, FirOverrideService(this)) register(FirOverrideService::class, FirOverrideService(this))
register(FirDynamicMembersStorage::class, FirDynamicMembersStorage(this)) register(FirDynamicMembersStorage::class, FirDynamicMembersStorage(this))
// TODO: replace with proper implementation
register(FirFunctionalTypeKindService::class, FirFunctionalTypeKindService.Default)
} }
@OptIn(SessionConfiguration::class) @OptIn(SessionConfiguration::class)
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.fir.resolve.providers.impl package org.jetbrains.kotlin.fir.resolve.providers.impl
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.fir.FirModuleData import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
@@ -25,16 +24,15 @@ import org.jetbrains.kotlin.fir.deserialization.FirBuiltinAnnotationDeserializer
import org.jetbrains.kotlin.fir.deserialization.FirConstDeserializer import org.jetbrains.kotlin.fir.deserialization.FirConstDeserializer
import org.jetbrains.kotlin.fir.deserialization.FirDeserializationContext import org.jetbrains.kotlin.fir.deserialization.FirDeserializationContext
import org.jetbrains.kotlin.fir.deserialization.deserializeClassToSymbol import org.jetbrains.kotlin.fir.deserialization.deserializeClassToSymbol
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotation
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyAnnotationArgumentMapping
import org.jetbrains.kotlin.fir.resolve.defaultType import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.constructClassType
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.fir.types.toLookupTag
import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.builtins.BuiltInsBinaryVersion import org.jetbrains.kotlin.metadata.builtins.BuiltInsBinaryVersion
import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl
@@ -53,7 +51,7 @@ open class FirBuiltinSymbolProvider(
val kotlinScopeProvider: FirKotlinScopeProvider val kotlinScopeProvider: FirKotlinScopeProvider
) : FirSymbolProvider(session) { ) : FirSymbolProvider(session) {
private val allPackageFragments = loadBuiltIns().groupBy { it.fqName } private val allPackageFragments = loadBuiltIns().groupBy { it.fqName }
private val syntheticFunctionalInterfaceCache = SyntheticFunctionalInterfaceCache(moduleData, kotlinScopeProvider) private val syntheticFunctionalInterfaceCache = SyntheticFunctionalInterfaceCache(session, moduleData, kotlinScopeProvider)
private fun loadBuiltIns(): List<BuiltInsPackageFragment> { private fun loadBuiltIns(): List<BuiltInsPackageFragment> {
val classLoader = this::class.java.classLoader val classLoader = this::class.java.classLoader
@@ -201,7 +199,11 @@ private data class BinaryVersionAndPackageFragment(
} }
} }
private class SyntheticFunctionalInterfaceCache(private val moduleData: FirModuleData, private val kotlinScopeProvider: FirKotlinScopeProvider) { private class SyntheticFunctionalInterfaceCache(
private val session: FirSession,
private val moduleData: FirModuleData,
private val kotlinScopeProvider: FirKotlinScopeProvider
) {
private val syntheticFunctionalInterfaceCache = private val syntheticFunctionalInterfaceCache =
moduleData.session.firCachesFactory.createCache(::createSyntheticFunctionalInterface) moduleData.session.firCachesFactory.createCache(::createSyntheticFunctionalInterface)
@@ -212,7 +214,7 @@ private class SyntheticFunctionalInterfaceCache(private val moduleData: FirModul
private fun createSyntheticFunctionalInterface(classId: ClassId): FirRegularClassSymbol? { private fun createSyntheticFunctionalInterface(classId: ClassId): FirRegularClassSymbol? {
return with(classId) { return with(classId) {
val className = relativeClassName.asString() val className = relativeClassName.asString()
val kind = FunctionClassKind.byClassNamePrefix(packageFqName, className) ?: return@with null val kind = session.functionalTypeService.getKindByClassNamePrefix(packageFqName, className) ?: return null
val prefix = kind.classNamePrefix val prefix = kind.classNamePrefix
val arity = className.substring(prefix.length).toIntOrNull() ?: return null val arity = className.substring(prefix.length).toIntOrNull() ?: return null
FirRegularClassSymbol(classId).apply symbol@{ FirRegularClassSymbol(classId).apply symbol@{
@@ -279,55 +281,30 @@ private class SyntheticFunctionalInterfaceCache(private val moduleData: FirModul
isTailRec = false isTailRec = false
isExternal = false isExternal = false
isSuspend = isSuspend =
kind == FunctionClassKind.SuspendFunction || kind == ConeFunctionalTypeKind.SuspendFunction ||
kind == FunctionClassKind.KSuspendFunction kind == ConeFunctionalTypeKind.KSuspendFunction
} }
val typeArguments = typeParameters.map { val typeArguments = typeParameters.map {
buildResolvedTypeRef { ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), false).toFirResolvedTypeRef()
type = ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), false)
}
} }
fun createSuperType( fun createSuperType(kind: ConeFunctionalTypeKind): FirResolvedTypeRef {
kind: FunctionClassKind, return kind.classId(arity).toLookupTag()
): FirResolvedTypeRef { .constructClassType(typeArguments.map { it.type }.toTypedArray(), isNullable = false)
return buildResolvedTypeRef { .toFirResolvedTypeRef()
type = kind.classId(arity).toLookupTag()
.constructClassType(typeArguments.map { it.type }.toTypedArray(), isNullable = false)
}
} }
superTypeRefs += when (kind) { if (kind.isReflectType) {
FunctionClassKind.Function -> listOf( superTypeRefs += StandardClassIds.KFunction.toLookupTag()
buildResolvedTypeRef { .constructClassType(arrayOf(typeArguments.last().type), isNullable = false)
type = StandardClassIds.Function.toLookupTag() .toFirResolvedTypeRef()
.constructClassType(arrayOf(typeArguments.last().type), isNullable = false) superTypeRefs += createSuperType(kind.nonReflectKind())
} } else {
) superTypeRefs += StandardClassIds.Function.toLookupTag()
.constructClassType(arrayOf(typeArguments.last().type), isNullable = false)
FunctionClassKind.SuspendFunction -> listOf( .toFirResolvedTypeRef()
buildResolvedTypeRef {
type = StandardClassIds.Function.toLookupTag()
.constructClassType(arrayOf(typeArguments.last().type), isNullable = false)
}
)
FunctionClassKind.KFunction -> listOf(
buildResolvedTypeRef {
type = StandardClassIds.KFunction.toLookupTag()
.constructClassType(arrayOf(typeArguments.last().type), isNullable = false)
},
createSuperType(FunctionClassKind.Function)
)
FunctionClassKind.KSuspendFunction -> listOf(
buildResolvedTypeRef {
type = StandardClassIds.KFunction.toLookupTag()
.constructClassType(arrayOf(typeArguments.last().type), isNullable = false)
},
createSuperType(FunctionClassKind.SuspendFunction)
)
} }
addDeclaration( addDeclaration(
buildSimpleFunction { buildSimpleFunction {
moduleData = this@SyntheticFunctionalInterfaceCache.moduleData moduleData = this@SyntheticFunctionalInterfaceCache.moduleData
@@ -357,6 +334,14 @@ private class SyntheticFunctionalInterfaceCache(private val moduleData: FirModul
} }
} }
dispatchReceiverType = classId.defaultType(this@klass.typeParameters.map { it.symbol }) dispatchReceiverType = classId.defaultType(this@klass.typeParameters.map { it.symbol })
kind.annotationOnInvokeClassId?.let { annotationClassId ->
annotations += buildAnnotation {
annotationTypeRef = annotationClassId
.constructClassLikeType(emptyArray(), isNullable = false)
.toFirResolvedTypeRef()
argumentMapping = FirEmptyAnnotationArgumentMapping
}
}
} }
) )
} }
@@ -364,6 +349,5 @@ private class SyntheticFunctionalInterfaceCache(private val moduleData: FirModul
} }
} }
private fun ConeFunctionalTypeKind.classId(arity: Int) = ClassId(packageFqName, numberedClassName(arity))
private fun FunctionClassKind.classId(arity: Int) = ClassId(packageFqName, numberedClassName(arity))
} }
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.serialization package org.jetbrains.kotlin.fir.serialization
import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.builtins.functions.FunctionalTypeKind
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
@@ -681,7 +682,7 @@ class FirElementSerializer private constructor(
return lowerBound return lowerBound
} }
is ConeClassLikeType -> { is ConeClassLikeType -> {
if (type.isSuspendFunctionType(session)) { if (type.functionalTypeKind(session) == FunctionalTypeKind.SuspendFunction) {
val runtimeFunctionType = type.suspendFunctionTypeToFunctionTypeWithContinuation( val runtimeFunctionType = type.suspendFunctionTypeToFunctionTypeWithContinuation(
session, StandardClassIds.Continuation session, StandardClassIds.Continuation
) )
@@ -5,11 +5,14 @@
package org.jetbrains.kotlin.fir.serialization package org.jetbrains.kotlin.fir.serialization
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic
import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.types.ConeErrorType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.types.typeContext import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.types.AbstractTypeApproximator import org.jetbrains.kotlin.types.AbstractTypeApproximator
import org.jetbrains.kotlin.types.model.SimpleTypeMarker import org.jetbrains.kotlin.types.model.SimpleTypeMarker
@@ -21,3 +24,22 @@ class TypeApproximatorForMetadataSerializer(session: FirSession) :
} }
} }
fun ConeKotlinType.suspendFunctionTypeToFunctionTypeWithContinuation(session: FirSession, continuationClassId: ClassId): ConeClassLikeType {
require(this.isSuspendFunctionType(session))
val kind =
if (isReflectFunctionalType(session)) FunctionClassKind.KFunction
else FunctionClassKind.Function
val fullyExpandedType = type.fullyExpandedType(session)
val typeArguments = fullyExpandedType.typeArguments
val functionalTypeId = ClassId(kind.packageFqName, kind.numberedClassName(typeArguments.size))
val lastTypeArgument = typeArguments.last()
return ConeClassLikeTypeImpl(
functionalTypeId.toLookupTag(),
typeArguments = (typeArguments.dropLast(1) + continuationClassId.toLookupTag().constructClassType(
arrayOf(lastTypeArgument),
isNullable = false
) + lastTypeArgument).toTypedArray(),
isNullable = fullyExpandedType.isNullable,
attributes = fullyExpandedType.attributes
)
}
@@ -233,8 +233,8 @@ class FirJvmSerializerExtension(
private fun FirFunction.needsInlineParameterNullCheckRequirement(): Boolean = private fun FirFunction.needsInlineParameterNullCheckRequirement(): Boolean =
this is FirSimpleFunction && isInline && !isSuspend && !isParamAssertionsDisabled && this is FirSimpleFunction && isInline && !isSuspend && !isParamAssertionsDisabled &&
!Visibilities.isPrivate(visibility) && !Visibilities.isPrivate(visibility) &&
(valueParameters.any { it.returnTypeRef.coneType.isBuiltinFunctionalType(session) } || (valueParameters.any { it.returnTypeRef.coneType.isSomeFunctionalType(session) } ||
receiverParameter?.typeRef?.coneType?.isBuiltinFunctionalType(session) == true) receiverParameter?.typeRef?.coneType?.isSomeFunctionalType(session) == true)
override fun serializeProperty( override fun serializeProperty(
property: FirProperty, property: FirProperty,
@@ -172,8 +172,8 @@ class FirJvmTypeMapper(val session: FirSession) : FirSessionComponent {
val parameters = classifier?.typeParameters.orEmpty().map { it.symbol } val parameters = classifier?.typeParameters.orEmpty().map { it.symbol }
val arguments = type.arguments val arguments = type.arguments
if ((defaultType.isFunctionalType(session) && arguments.size > BuiltInFunctionArity.BIG_ARITY) if ((defaultType.isSimpleFunctionType(session) && arguments.size > BuiltInFunctionArity.BIG_ARITY)
|| defaultType.isKFunctionType(session) || defaultType.isReflectFunctionalType(session)
) { ) {
writeGenericArguments(sw, listOf(arguments.last()), listOf(parameters.last()), mode) writeGenericArguments(sw, listOf(arguments.last()), listOf(parameters.last()), mode)
return return
@@ -264,7 +264,7 @@ class ConeTypeSystemCommonBackendContextForTypeMapping(
override fun SimpleTypeMarker.isSuspendFunction(): Boolean { override fun SimpleTypeMarker.isSuspendFunction(): Boolean {
require(this is ConeSimpleKotlinType) require(this is ConeSimpleKotlinType)
return isSuspendOrKSuspendFunctionType(session) return isSuspendFunctionType(session)
} }
override fun SimpleTypeMarker.isKClass(): Boolean { override fun SimpleTypeMarker.isKClass(): Boolean {
@@ -538,7 +538,7 @@ class Fir2IrDeclarationStorage(
?: if (isLambda) SpecialNames.ANONYMOUS else Name.special("<no name provided>") ?: if (isLambda) SpecialNames.ANONYMOUS else Name.special("<no name provided>")
val visibility = simpleFunction?.visibility ?: Visibilities.Local val visibility = simpleFunction?.visibility ?: Visibilities.Local
val isSuspend = val isSuspend =
if (isLambda) ((function as FirAnonymousFunction).typeRef as? FirResolvedTypeRef)?.type?.isSuspendOrKSuspendFunctionType(session) == true if (isLambda) ((function as FirAnonymousFunction).typeRef as? FirResolvedTypeRef)?.type?.isSuspendFunctionType(session) == true
else simpleFunction?.isSuspend == true else simpleFunction?.isSuspend == true
val created = function.convertWithOffsets { startOffset, endOffset -> val created = function.convertWithOffsets { startOffset, endOffset ->
val result = declareIrSimpleFunction(signature, simpleFunction?.containerSource) { symbol -> val result = declareIrSimpleFunction(signature, simpleFunction?.containerSource) { symbol ->
@@ -134,7 +134,7 @@ internal class AdapterGenerator(
} }
internal fun ConeKotlinType.kFunctionTypeToFunctionType(): IrSimpleType = internal fun ConeKotlinType.kFunctionTypeToFunctionType(): IrSimpleType =
kFunctionTypeToFunctionType(session).toIrType() as IrSimpleType reflectFunctionalTypeToNonReflectFunctionalType(session).toIrType() as IrSimpleType
internal fun generateAdaptedCallableReference( internal fun generateAdaptedCallableReference(
callableReferenceAccess: FirCallableReferenceAccess, callableReferenceAccess: FirCallableReferenceAccess,
@@ -479,7 +479,7 @@ internal class AdapterGenerator(
} }
// If the expected type is a built-in functional type, we don't need SAM conversion. // If the expected type is a built-in functional type, we don't need SAM conversion.
val expectedType = argument.getExpectedType(parameter) val expectedType = argument.getExpectedType(parameter)
if (expectedType is ConeTypeParameterType || expectedType.isBuiltinFunctionalType(session)) { if (expectedType is ConeTypeParameterType || expectedType.isSomeFunctionalType(session)) {
return false return false
} }
// On the other hand, the actual type should be either a functional type or a subtype of a class that has a contributed `invoke`. // On the other hand, the actual type should be either a functional type or a subtype of a class that has a contributed `invoke`.
@@ -514,10 +514,10 @@ internal class AdapterGenerator(
return this return this
} }
// Expect the expected type to be a suspend functional type. // Expect the expected type to be a suspend functional type.
if (!parameterType.isSuspendOrKSuspendFunctionType(session)) { if (!parameterType.isSuspendFunctionType(session)) {
return this return this
} }
val expectedFunctionalType = parameterType.suspendFunctionTypeToFunctionType(session) val expectedFunctionalType = parameterType.customFunctionalTypeToSimpleFunctionalType(session)
if (this is IrVararg) { if (this is IrVararg) {
// element-wise conversion if and only if we can build 1-to-1 mapping for elements. // element-wise conversion if and only if we can build 1-to-1 mapping for elements.
return applyConversionOnVararg(argument) { firVarargArgument -> return applyConversionOnVararg(argument) { firVarargArgument ->
@@ -546,9 +546,9 @@ internal class AdapterGenerator(
argument: FirExpression argument: FirExpression
): IrSimpleFunctionSymbol? { ): IrSimpleFunctionSymbol? {
val argumentType = argument.typeRef.coneType val argumentType = argument.typeRef.coneType
val argumentTypeWithInvoke = argumentType.findSubtypeOfNonSuspendFunctionalType(session, expectedFunctionalType) ?: return null val argumentTypeWithInvoke = argumentType.findSubtypeOfSimpleFunctionalType(session, expectedFunctionalType) ?: return null
return if (argumentTypeWithInvoke.isBuiltinFunctionalType(session)) { return if (argumentTypeWithInvoke.isSomeFunctionalType(session)) {
(argumentTypeWithInvoke as? ConeClassLikeType)?.findBaseInvokeSymbol(session, scopeSession) (argumentTypeWithInvoke as? ConeClassLikeType)?.findBaseInvokeSymbol(session, scopeSession)
} else { } else {
argumentTypeWithInvoke.findContributedInvokeSymbol( argumentTypeWithInvoke.findContributedInvokeSymbol(
@@ -188,7 +188,7 @@ class CallAndReferenceGenerator(
// This would cause ClassCastException in case of usual type approximation, // This would cause ClassCastException in case of usual type approximation,
// because '{ X1 & ... & Xm }' would be approximated to 'Nothing'. // because '{ X1 & ... & Xm }' would be approximated to 'Nothing'.
// JVM_OLD just relies on type mapping for generic argument types in such case. // JVM_OLD just relies on type mapping for generic argument types in such case.
if (!kotlinType.isKFunctionType(session)) if (!kotlinType.isReflectFunctionalType(session))
return kotlinType return kotlinType
if (kotlinType !is ConeSimpleKotlinType) if (kotlinType !is ConeSimpleKotlinType)
return kotlinType return kotlinType
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.fir.resolve.providers.impl package org.jetbrains.kotlin.fir.resolve.providers.impl
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.NoMutableState import org.jetbrains.kotlin.fir.NoMutableState
import org.jetbrains.kotlin.fir.caches.FirCache import org.jetbrains.kotlin.fir.caches.FirCache
@@ -19,6 +18,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.functionalTypeService
import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -116,7 +116,7 @@ class FirCachingCompositeSymbolProvider(
} }
private fun isNameForFunctionClass(classId: ClassId): Boolean { private fun isNameForFunctionClass(classId: ClassId): Boolean {
return FunctionClassKind.byClassNamePrefix(classId.packageFqName, classId.shortClassName.asString()) != null return session.functionalTypeService.getKindByClassNamePrefix(classId.packageFqName, classId.shortClassName.asString()) != null
} }
@OptIn(FirSymbolProviderInternals::class) @OptIn(FirSymbolProviderInternals::class)
@@ -66,7 +66,7 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
): SimpleTypeMarker { ): SimpleTypeMarker {
val attributesList = attributes?.filterIsInstanceTo<ConeAttribute<*>, MutableList<ConeAttribute<*>>>(mutableListOf()) val attributesList = attributes?.filterIsInstanceTo<ConeAttribute<*>, MutableList<ConeAttribute<*>>>(mutableListOf())
val coneAttributes: ConeAttributes = if (isExtensionFunction) { val coneAttributes: ConeAttributes = if (isExtensionFunction) {
require(constructor is ConeClassLikeLookupTag && constructor.isBuiltinFunctionalType()) require(constructor is ConeClassLikeLookupTag && constructor.isSomeFunctionalType(session))
// We don't want to create new instance of ConeAttributes which // We don't want to create new instance of ConeAttributes which
// contains only CompilerConeAttributes.ExtensionFunctionType // contains only CompilerConeAttributes.ExtensionFunctionType
// to avoid memory consumption // to avoid memory consumption
@@ -226,7 +226,7 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
override fun KotlinTypeMarker.isBuiltinFunctionalTypeOrSubtype(): Boolean { override fun KotlinTypeMarker.isBuiltinFunctionalTypeOrSubtype(): Boolean {
require(this is ConeKotlinType) require(this is ConeKotlinType)
return this.isTypeOrSubtypeOf { return this.isTypeOrSubtypeOf {
(it.lowerBoundIfFlexible() as ConeKotlinType).isBuiltinFunctionalType(session) (it.lowerBoundIfFlexible() as ConeKotlinType).isSomeFunctionalType(session)
} }
} }
@@ -463,7 +463,7 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
override fun KotlinTypeMarker.isFunctionOrKFunctionWithAnySuspendability(): Boolean { override fun KotlinTypeMarker.isFunctionOrKFunctionWithAnySuspendability(): Boolean {
require(this is ConeKotlinType) require(this is ConeKotlinType)
return this.isBuiltinFunctionalType(session) return this.isSomeFunctionalType(session)
} }
private fun ConeKotlinType.isTypeOrSubtypeOf(predicate: (ConeKotlinType) -> Boolean): Boolean { private fun ConeKotlinType.isTypeOrSubtypeOf(predicate: (ConeKotlinType) -> Boolean): Boolean {
@@ -519,13 +519,13 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
return fullyExpandedType(session).let { return fullyExpandedType(session).let {
val simpleType = it.lowerBoundIfFlexible() val simpleType = it.lowerBoundIfFlexible()
if ((simpleType as ConeKotlinType).isBuiltinFunctionalType(session)) if ((simpleType as ConeKotlinType).isSomeFunctionalType(session))
this this
else { else {
var functionalSupertype: KotlinTypeMarker? = null var functionalSupertype: KotlinTypeMarker? = null
simpleType.anySuperTypeConstructor { type -> simpleType.anySuperTypeConstructor { type ->
simpleType.fastCorrespondingSupertypes(type.typeConstructor())?.any { superType -> simpleType.fastCorrespondingSupertypes(type.typeConstructor())?.any { superType ->
val isFunctional = (superType as ConeKotlinType).isBuiltinFunctionalType(session) val isFunctional = (superType as ConeKotlinType).isSomeFunctionalType(session)
if (isFunctional) if (isFunctional)
functionalSupertype = superType functionalSupertype = superType
isFunctional isFunctional
@@ -0,0 +1,248 @@
/*
* 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.
*/
package org.jetbrains.kotlin.fir.types
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.originalForSubstitutionOverride
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.scope
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.runUnless
// ---------------------------------------------- is type is a functional type ----------------------------------------------
fun ConeKotlinType.functionalTypeKind(session: FirSession): ConeFunctionalTypeKind? {
if (this !is ConeClassLikeType) return null
return fullyExpandedType(session).lookupTag.functionalTypeKind(session)
}
private fun ConeClassLikeLookupTag.functionalTypeKind(session: FirSession): ConeFunctionalTypeKind? {
val classId = classId
return session.functionalTypeService.getKindByClassNamePrefix(classId.packageFqName, classId.shortClassName.asString())
}
private inline fun ConeKotlinType.isFunctionalTypeWithPredicate(
session: FirSession,
errorOnNotFunctionalType: Boolean = false,
predicate: (ConeFunctionalTypeKind) -> Boolean
): Boolean {
val kind = functionalTypeKind(session)
?: if (errorOnNotFunctionalType) error("$this is not a functional type") else return false
return predicate(kind)
}
// Function
fun ConeKotlinType.isSimpleFunctionType(session: FirSession): Boolean {
return isFunctionalTypeWithPredicate(session) { it == ConeFunctionalTypeKind.Function }
}
// Function, SuspendFunction, [Custom]Function
fun ConeKotlinType.isNonReflectFunctionalType(session: FirSession): Boolean {
return isFunctionalTypeWithPredicate(session) { !it.isReflectType }
}
// SuspendFunction, KSuspendFunction
fun ConeKotlinType.isSuspendFunctionType(session: FirSession): Boolean {
return isFunctionalTypeWithPredicate(session) {
it == ConeFunctionalTypeKind.SuspendFunction || it == ConeFunctionalTypeKind.KSuspendFunction
}
}
// KFunction, KSuspendFunction, K[Custom]Function
fun ConeKotlinType.isReflectFunctionalType(session: FirSession): Boolean {
return isFunctionalTypeWithPredicate(session) { it.isReflectType }
}
// Function, SuspendFunction, [Custom]Function, KFunction, KSuspendFunction, K[Custom]Function
fun ConeKotlinType.isSomeFunctionalType(session: FirSession): Boolean {
return functionalTypeKind(session) != null
}
// Function, SuspendFunction, [Custom]Function, KFunction, KSuspendFunction, K[Custom]Function
fun ConeClassLikeLookupTag.isSomeFunctionalType(session: FirSession): Boolean {
return functionalTypeKind(session) != null
}
// Function, KFunction
private fun ConeKotlinType.isSimpleFunctionalType(session: FirSession, errorOnNotFunctionalType: Boolean): Boolean {
return isFunctionalTypeWithPredicate(session, errorOnNotFunctionalType) {
it == ConeFunctionalTypeKind.Function || it == ConeFunctionalTypeKind.KFunction
}
}
// SuspendFunction, [Custom]Function, KSuspendFunction, K[Custom]Function
private fun ConeKotlinType.isNotSimpleFunctionalType(session: FirSession): Boolean {
return !isSimpleFunctionalType(session, errorOnNotFunctionalType = false)
}
// ---------------------------------------------- functional type conversions ----------------------------------------------
/*
* SuspendFunction/[Custom]Function -> Function
* KSuspendFunction/K[Custom]Function -> KFunction
*/
fun ConeKotlinType.customFunctionalTypeToSimpleFunctionalType(session: FirSession): ConeClassLikeType {
val kind = functionalTypeKind(session)
require(kind != null && kind != ConeFunctionalTypeKind.Function && kind != ConeFunctionalTypeKind.KFunction)
val newKind = if (kind.isReflectType) {
ConeFunctionalTypeKind.KFunction
} else {
ConeFunctionalTypeKind.Function
}
return createFunctionalTypeWithNewKind(newKind)
}
/*
* KFunction -> Function
* KSuspendFunction -> SuspendFunction
* K[Custom]Function -> [Custom]Function
*/
fun ConeKotlinType.reflectFunctionalTypeToNonReflectFunctionalType(session: FirSession): ConeClassLikeType {
val kind = functionalTypeKind(session)
require(kind != null && kind.isReflectType)
return createFunctionalTypeWithNewKind(kind.nonReflectKind())
}
private fun ConeKotlinType.createFunctionalTypeWithNewKind(kind: ConeFunctionalTypeKind): ConeClassLikeType {
val functionalTypeId = ClassId(kind.packageFqName, kind.numberedClassName(typeArguments.size - 1))
return functionalTypeId.toLookupTag().constructClassType(typeArguments, isNullable = false, attributes = attributes)
}
// ---------------------------------------------- functional type subtyping ----------------------------------------------
// expectedFunctionalType is kotlin.FunctionN or kotlin.reflect.KFunctionN
fun ConeKotlinType.findSubtypeOfSimpleFunctionalType(session: FirSession, expectedFunctionalType: ConeClassLikeType): ConeKotlinType? {
require(expectedFunctionalType.isSimpleFunctionalType(session, errorOnNotFunctionalType = true))
return findSubtypeOfSimpleFunctionalTypeImpl(session, expectedFunctionalType)
}
private fun ConeKotlinType.findSubtypeOfSimpleFunctionalTypeImpl(
session: FirSession,
expectedFunctionalType: ConeClassLikeType
): ConeKotlinType? {
return when (this) {
is ConeClassLikeType -> {
// Expect the argument type is a simple functional type.
when {
isNotSimpleFunctionalType(session) -> null
isSubtypeOfFunctionalType(session, expectedFunctionalType) -> this
else -> null
}
}
is ConeIntersectionType -> {
runUnless(intersectedTypes.any { it.isNotSimpleFunctionalType(session) }) {
intersectedTypes.find { it.findSubtypeOfSimpleFunctionalTypeImpl(session, expectedFunctionalType) != null }
}
}
is ConeTypeParameterType -> {
val bounds = lookupTag.typeParameterSymbol.resolvedBounds.map { it.coneType }
runUnless(bounds.any { it.isNotSimpleFunctionalType(session) }) {
bounds.find { it.findSubtypeOfSimpleFunctionalTypeImpl(session, expectedFunctionalType) != null }
}
}
else -> null
}
}
private fun ConeKotlinType.isSubtypeOfFunctionalType(session: FirSession, expectedFunctionalType: ConeClassLikeType): Boolean {
return AbstractTypeChecker.isSubtypeOf(session.typeContext, this, expectedFunctionalType.replaceArgumentsWithStarProjections())
}
// ---------------------------------------------- functional type scope utils ----------------------------------------------
fun ConeClassLikeType.findBaseInvokeSymbol(session: FirSession, scopeSession: ScopeSession): FirNamedFunctionSymbol? {
require(this.isSomeFunctionalType(session))
val functionN = (lookupTag.toSymbol(session)?.fir as? FirClass) ?: return null
var baseInvokeSymbol: FirNamedFunctionSymbol? = null
functionN.unsubstitutedScope(
session,
scopeSession,
withForcedTypeCalculator = false
).processFunctionsByName(OperatorNameConventions.INVOKE) { functionSymbol ->
baseInvokeSymbol = functionSymbol
return@processFunctionsByName
}
return baseInvokeSymbol
}
fun ConeKotlinType.findContributedInvokeSymbol(
session: FirSession,
scopeSession: ScopeSession,
expectedFunctionalType: ConeClassLikeType,
shouldCalculateReturnTypesOfFakeOverrides: Boolean
): FirFunctionSymbol<*>? {
val baseInvokeSymbol = expectedFunctionalType.findBaseInvokeSymbol(session, scopeSession) ?: return null
val fakeOverrideTypeCalculator = if (shouldCalculateReturnTypesOfFakeOverrides) {
FakeOverrideTypeCalculator.Forced
} else {
FakeOverrideTypeCalculator.DoNothing
}
val scope = scope(session, scopeSession, fakeOverrideTypeCalculator, requiredPhase = FirResolvePhase.STATUS) ?: return null
var declaredInvoke: FirNamedFunctionSymbol? = null
scope.processFunctionsByName(OperatorNameConventions.INVOKE) { functionSymbol ->
if (functionSymbol.fir.valueParameters.size == baseInvokeSymbol.fir.valueParameters.size) {
declaredInvoke = functionSymbol
return@processFunctionsByName
}
}
var overriddenInvoke: FirFunctionSymbol<*>? = null
if (declaredInvoke != null) {
// Make sure the user-contributed or type-substituted invoke we just found above is an override of base invoke.
scope.processOverriddenFunctions(declaredInvoke!!) { functionSymbol ->
if (functionSymbol == baseInvokeSymbol || functionSymbol.originalForSubstitutionOverride == baseInvokeSymbol) {
overriddenInvoke = functionSymbol
ProcessorAction.STOP
} else {
ProcessorAction.NEXT
}
}
}
return if (overriddenInvoke != null) declaredInvoke else null
}
// ---------------------------------------------- functional type type argument extraction ----------------------------------------------
fun ConeKotlinType.receiverType(session: FirSession): ConeKotlinType? {
if (!isSomeFunctionalType(session) || !isExtensionFunctionType(session)) return null
return fullyExpandedType(session).let { expanded ->
expanded.typeArguments[expanded.contextReceiversNumberForFunctionType].typeOrDefault(session.builtinTypes.nothingType.type)
}
}
fun ConeKotlinType.returnType(session: FirSession): ConeKotlinType {
require(this is ConeClassLikeType)
// TODO: add requirement
return fullyExpandedType(session).typeArguments.last().typeOrDefault(session.builtinTypes.nullableAnyType.type)
}
fun ConeKotlinType.valueParameterTypesIncludingReceiver(session: FirSession): List<ConeKotlinType> {
require(this is ConeClassLikeType)
// TODO: add requirement
return fullyExpandedType(session).typeArguments.dropLast(1).map { it.typeOrDefault(session.builtinTypes.nothingType.type) }
}
private fun ConeTypeProjection.typeOrDefault(default: ConeKotlinType): ConeKotlinType = when (this) {
is ConeKotlinTypeProjection -> type
is ConeStarProjection -> default
}
@@ -5,27 +5,12 @@
package org.jetbrains.kotlin.fir.types package org.jetbrains.kotlin.fir.types
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.originalForSubstitutionOverride
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.scope
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.util.OperatorNameConventions
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract import kotlin.contracts.contract
@@ -50,203 +35,10 @@ fun ConeKotlinType.isKMutableProperty(session: FirSession): Boolean {
classId.shortClassName.identifier.startsWith("KMutableProperty") classId.shortClassName.identifier.startsWith("KMutableProperty")
} }
fun ConeKotlinType.functionClassKind(session: FirSession): FunctionClassKind? {
return classId(session)?.toFunctionClassKind()
}
private fun ClassId.toFunctionClassKind(): FunctionClassKind? {
return FunctionClassKind.byClassNamePrefix(packageFqName, relativeClassName.asString())
}
// Function, SuspendFunction, KFunction, KSuspendFunction
fun ConeKotlinType.isBuiltinFunctionalType(session: FirSession): Boolean {
return functionClassKind(session) != null
}
// Function, SuspendFunction, KFunction, KSuspendFunction
fun ConeClassLikeLookupTag.isBuiltinFunctionalType(): Boolean {
return classId.toFunctionClassKind() != null
}
inline fun ConeKotlinType.isFunctionalType(session: FirSession, predicate: (FunctionClassKind) -> Boolean): Boolean {
val kind = functionClassKind(session) ?: return false
return predicate(kind)
}
// Function
fun ConeKotlinType.isFunctionalType(session: FirSession): Boolean {
return isFunctionalType(session) { it == FunctionClassKind.Function }
}
// Function, SuspendFunction
fun ConeKotlinType.isFunctionalOrSuspendFunctionalType(session: FirSession): Boolean {
return isFunctionalType(session) { it == FunctionClassKind.Function || it == FunctionClassKind.SuspendFunction }
}
// SuspendFunction, KSuspendFunction
fun ConeKotlinType.isSuspendOrKSuspendFunctionType(session: FirSession): Boolean {
return isFunctionalType(session) { it.isSuspendType }
}
fun ConeKotlinType.isSuspendFunctionType(session: FirSession): Boolean {
return isFunctionalType(session) { it == FunctionClassKind.SuspendFunction }
}
// KFunction, KSuspendFunction
fun ConeKotlinType.isKFunctionType(session: FirSession): Boolean {
return isFunctionalType(session) { it.isReflectType }
}
fun ConeKotlinType.kFunctionTypeToFunctionType(session: FirSession): ConeClassLikeType {
require(this.isKFunctionType(session))
val kind =
if (isSuspendOrKSuspendFunctionType(session)) FunctionClassKind.SuspendFunction
else FunctionClassKind.Function
val functionalTypeId = ClassId(kind.packageFqName, kind.numberedClassName(typeArguments.size - 1))
return ConeClassLikeTypeImpl(functionalTypeId.toLookupTag(), typeArguments, isNullable = false)
}
fun ConeKotlinType.suspendFunctionTypeToFunctionType(session: FirSession): ConeClassLikeType {
require(this.isSuspendOrKSuspendFunctionType(session))
val kind =
if (isKFunctionType(session)) FunctionClassKind.KFunction
else FunctionClassKind.Function
val functionalTypeId = ClassId(kind.packageFqName, kind.numberedClassName(typeArguments.size - 1))
return ConeClassLikeTypeImpl(functionalTypeId.toLookupTag(), typeArguments, isNullable = false, attributes = attributes)
}
fun ConeKotlinType.suspendFunctionTypeToFunctionTypeWithContinuation(session: FirSession, continuationClassId: ClassId): ConeClassLikeType {
require(this.isSuspendOrKSuspendFunctionType(session))
val kind =
if (isKFunctionType(session)) FunctionClassKind.KFunction
else FunctionClassKind.Function
val fullyExpandedType = type.fullyExpandedType(session)
val typeArguments = fullyExpandedType.typeArguments
val functionalTypeId = ClassId(kind.packageFqName, kind.numberedClassName(typeArguments.size))
val lastTypeArgument = typeArguments.last()
return ConeClassLikeTypeImpl(
functionalTypeId.toLookupTag(),
typeArguments = (typeArguments.dropLast(1) + continuationClassId.toLookupTag().constructClassType(
arrayOf(lastTypeArgument),
isNullable = false
) + lastTypeArgument).toTypedArray(),
isNullable = fullyExpandedType.isNullable,
attributes = fullyExpandedType.attributes
)
}
fun ConeKotlinType.isSubtypeOfFunctionalType(session: FirSession, expectedFunctionalType: ConeClassLikeType): Boolean {
require(expectedFunctionalType.isBuiltinFunctionalType(session))
return AbstractTypeChecker.isSubtypeOf(session.typeContext, this, expectedFunctionalType.replaceArgumentsWithStarProjections())
}
fun ConeKotlinType.findSubtypeOfNonSuspendFunctionalType(session: FirSession, expectedFunctionalType: ConeClassLikeType): ConeKotlinType? {
require(expectedFunctionalType.isBuiltinFunctionalType(session) && !expectedFunctionalType.isSuspendOrKSuspendFunctionType(session))
return when (this) {
is ConeClassLikeType -> {
// Expect the argument type is not a suspend functional type.
if (isSuspendOrKSuspendFunctionType(session) || !isSubtypeOfFunctionalType(session, expectedFunctionalType))
null
else
this
}
is ConeIntersectionType -> {
if (intersectedTypes.any { it.isSuspendOrKSuspendFunctionType(session) })
null
else
intersectedTypes.find { it.findSubtypeOfNonSuspendFunctionalType(session, expectedFunctionalType) != null }
}
is ConeTypeParameterType -> {
val bounds = lookupTag.typeParameterSymbol.resolvedBounds.map { it.coneType }
if (bounds.any { it.isSuspendOrKSuspendFunctionType(session) })
null
else
bounds.find { it.findSubtypeOfNonSuspendFunctionalType(session, expectedFunctionalType) != null }
}
else -> null
}
}
fun ConeClassLikeType.findBaseInvokeSymbol(session: FirSession, scopeSession: ScopeSession): FirNamedFunctionSymbol? {
require(this.isBuiltinFunctionalType(session))
val functionN = (lookupTag.toSymbol(session)?.fir as? FirClass) ?: return null
var baseInvokeSymbol: FirNamedFunctionSymbol? = null
functionN.unsubstitutedScope(
session,
scopeSession,
withForcedTypeCalculator = false
).processFunctionsByName(OperatorNameConventions.INVOKE) { functionSymbol ->
baseInvokeSymbol = functionSymbol
return@processFunctionsByName
}
return baseInvokeSymbol
}
fun ConeKotlinType.findContributedInvokeSymbol(
session: FirSession,
scopeSession: ScopeSession,
expectedFunctionalType: ConeClassLikeType,
shouldCalculateReturnTypesOfFakeOverrides: Boolean
): FirFunctionSymbol<*>? {
val baseInvokeSymbol = expectedFunctionalType.findBaseInvokeSymbol(session, scopeSession) ?: return null
val fakeOverrideTypeCalculator = if (shouldCalculateReturnTypesOfFakeOverrides) {
FakeOverrideTypeCalculator.Forced
} else {
FakeOverrideTypeCalculator.DoNothing
}
val scope = scope(session, scopeSession, fakeOverrideTypeCalculator, requiredPhase = null) ?: return null
var declaredInvoke: FirNamedFunctionSymbol? = null
scope.processFunctionsByName(OperatorNameConventions.INVOKE) { functionSymbol ->
if (functionSymbol.fir.valueParameters.size == baseInvokeSymbol.fir.valueParameters.size) {
declaredInvoke = functionSymbol
return@processFunctionsByName
}
}
var overriddenInvoke: FirFunctionSymbol<*>? = null
if (declaredInvoke != null) {
// Make sure the user-contributed or type-substituted invoke we just found above is an override of base invoke.
scope.processOverriddenFunctions(declaredInvoke!!) { functionSymbol ->
if (functionSymbol == baseInvokeSymbol || functionSymbol.originalForSubstitutionOverride == baseInvokeSymbol) {
overriddenInvoke = functionSymbol
ProcessorAction.STOP
} else {
ProcessorAction.NEXT
}
}
}
return if (overriddenInvoke != null) declaredInvoke else null
}
fun ConeKotlinType.isKClassType(): Boolean { fun ConeKotlinType.isKClassType(): Boolean {
return classId == StandardClassIds.KClass return classId == StandardClassIds.KClass
} }
private fun ConeTypeProjection.typeOrDefault(default: ConeKotlinType): ConeKotlinType =
when (this) {
is ConeKotlinTypeProjection -> type
is ConeStarProjection -> default
}
fun ConeKotlinType.receiverType(session: FirSession): ConeKotlinType? {
if (!isBuiltinFunctionalType(session) || !isExtensionFunctionType(session)) return null
return fullyExpandedType(session).let { expanded ->
expanded.typeArguments[expanded.contextReceiversNumberForFunctionType].typeOrDefault(session.builtinTypes.nothingType.type)
}
}
fun ConeKotlinType.returnType(session: FirSession): ConeKotlinType {
require(this is ConeClassLikeType)
return fullyExpandedType(session).typeArguments.last().typeOrDefault(session.builtinTypes.nullableAnyType.type)
}
fun ConeKotlinType.valueParameterTypesIncludingReceiver(session: FirSession): List<ConeKotlinType> {
require(this is ConeClassLikeType)
return fullyExpandedType(session).typeArguments.dropLast(1).map { it.typeOrDefault(session.builtinTypes.nothingType.type) }
}
val FirAnonymousFunction.returnType: ConeKotlinType? get() = returnTypeRef.coneTypeSafe() val FirAnonymousFunction.returnType: ConeKotlinType? get() = returnTypeRef.coneTypeSafe()
val FirAnonymousFunction.receiverType: ConeKotlinType? get() = receiverParameter?.typeRef?.coneTypeSafe() val FirAnonymousFunction.receiverType: ConeKotlinType? get() = receiverParameter?.typeRef?.coneTypeSafe()
@@ -18,7 +18,7 @@ import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer
import org.jetbrains.kotlin.fir.types.classId import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isBuiltinFunctionalType import org.jetbrains.kotlin.fir.types.isSomeFunctionalType
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
import org.jetbrains.kotlin.resolve.descriptorUtil.OVERLOAD_RESOLUTION_BY_LAMBDA_ANNOTATION_CLASS_ID import org.jetbrains.kotlin.resolve.descriptorUtil.OVERLOAD_RESOLUTION_BY_LAMBDA_ANNOTATION_CLASS_ID
import org.jetbrains.kotlin.utils.addToStdlib.same import org.jetbrains.kotlin.utils.addToStdlib.same
@@ -104,7 +104,7 @@ class FirOverloadByLambdaReturnTypeResolver(
.values.singleOrNull()?.toMap() ?: return null .values.singleOrNull()?.toMap() ?: return null
if (!lambdas.values.same { it.parameters.size }) return null if (!lambdas.values.same { it.parameters.size }) return null
if (!lambdas.values.all { it.expectedType?.isBuiltinFunctionalType(session) == true }) return null if (!lambdas.values.all { it.expectedType?.isSomeFunctionalType(session) == true }) return null
val originalCalleeReference = call.calleeReference val originalCalleeReference = call.calleeReference
@@ -56,6 +56,7 @@ import org.jetbrains.kotlin.types.ConstantValueKind
import org.jetbrains.kotlin.types.SmartcastStability import org.jetbrains.kotlin.types.SmartcastStability
import org.jetbrains.kotlin.types.model.safeSubstitute import org.jetbrains.kotlin.types.model.safeSubstitute
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract import kotlin.contracts.contract
@@ -581,18 +582,15 @@ fun FirFunction.getAsForbiddenNamedArgumentsTarget(
result result
} }
// referenced function of a Kotlin function type // referenced function of a Kotlin function type
FirDeclarationOrigin.BuiltIns -> { FirDeclarationOrigin.BuiltIns -> runIf(dispatchReceiverClassLookupTagOrNull()?.isSomeFunctionalType(session) == true) {
if (dispatchReceiverClassLookupTagOrNull()?.isBuiltinFunctionalType() == true) { ForbiddenNamedArgumentsTarget.INVOKE_ON_FUNCTION_TYPE
ForbiddenNamedArgumentsTarget.INVOKE_ON_FUNCTION_TYPE
} else {
null
}
} }
FirDeclarationOrigin.Synthetic -> null
FirDeclarationOrigin.DynamicScope -> null FirDeclarationOrigin.Synthetic,
FirDeclarationOrigin.RenamedForOverride -> null FirDeclarationOrigin.DynamicScope,
FirDeclarationOrigin.WrappedIntegerOperator -> null FirDeclarationOrigin.RenamedForOverride,
FirDeclarationOrigin.ScriptCustomization -> null FirDeclarationOrigin.WrappedIntegerOperator,
FirDeclarationOrigin.ScriptCustomization,
is FirDeclarationOrigin.Plugin -> null // TODO: figure out what to do with plugin generated functions is FirDeclarationOrigin.Plugin -> null // TODO: figure out what to do with plugin generated functions
} }
} }
@@ -293,9 +293,9 @@ private fun argumentTypeWithSuspendConversion(
} }
// We want to check the argument type against non-suspend functional type. // We want to check the argument type against non-suspend functional type.
val expectedFunctionalType = expectedType.suspendFunctionTypeToFunctionType(session) val expectedFunctionalType = expectedType.customFunctionalTypeToSimpleFunctionalType(session)
val argumentTypeWithInvoke = argumentType.findSubtypeOfNonSuspendFunctionalType(session, expectedFunctionalType) val argumentTypeWithInvoke = argumentType.findSubtypeOfSimpleFunctionalType(session, expectedFunctionalType)
return argumentTypeWithInvoke?.findContributedInvokeSymbol( return argumentTypeWithInvoke?.findContributedInvokeSymbol(
session, session,
@@ -308,7 +308,7 @@ private fun argumentTypeWithSuspendConversion(
null, null,
invokeSymbol.fir.returnTypeRef.coneType, invokeSymbol.fir.returnTypeRef.coneType,
isSuspend = true, isSuspend = true,
isKFunctionType = argumentType.isKFunctionType(session) isKFunctionType = argumentType.isReflectFunctionalType(session)
) )
} }
} }
@@ -498,7 +498,7 @@ private fun Candidate.getExpectedTypeWithSAMConversion(
candidateExpectedType: ConeKotlinType, candidateExpectedType: ConeKotlinType,
context: ResolutionContext context: ResolutionContext
): ConeKotlinType? { ): ConeKotlinType? {
if (candidateExpectedType.isBuiltinFunctionalType(session)) return null if (candidateExpectedType.isSomeFunctionalType(session)) return null
// TODO: resolvedCall.registerArgumentWithSamConversion(argument, SamConversionDescription(convertedTypeByOriginal, convertedTypeByCandidate!!)) // TODO: resolvedCall.registerArgumentWithSamConversion(argument, SamConversionDescription(convertedTypeByOriginal, convertedTypeByCandidate!!))
@@ -521,7 +521,7 @@ fun FirExpression.isFunctional(
else -> { else -> {
// Either a functional type or a subtype of a class that has a contributed `invoke`. // Either a functional type or a subtype of a class that has a contributed `invoke`.
val coneType = typeRef.coneTypeSafe<ConeKotlinType>() ?: return false val coneType = typeRef.coneTypeSafe<ConeKotlinType>() ?: return false
if (coneType.isBuiltinFunctionalType(session)) { if (coneType.isSomeFunctionalType(session)) {
return true return true
} }
val classLikeExpectedFunctionType = expectedFunctionType?.lowerBoundIfFlexible() as? ConeClassLikeType val classLikeExpectedFunctionType = expectedFunctionType?.lowerBoundIfFlexible() as? ConeClassLikeType
@@ -297,7 +297,7 @@ object CheckDslScopeViolation : ResolutionStage() {
// ``` // ```
// `useX()` is a call to `invoke` with `useX` as the dispatch receiver. In the FIR tree, extension receiver is represented as an // `useX()` is a call to `invoke` with `useX` as the dispatch receiver. In the FIR tree, extension receiver is represented as an
// implicit `this` expression passed as the first argument. // implicit `this` expression passed as the first argument.
if (candidate.dispatchReceiverValue?.type?.fullyExpandedType(context.session)?.isBuiltinFunctionalType(context.session) == true && if (candidate.dispatchReceiverValue?.type?.fullyExpandedType(context.session)?.isSomeFunctionalType(context.session) == true &&
(candidate.symbol as? FirNamedFunctionSymbol)?.name == OperatorNameConventions.INVOKE (candidate.symbol as? FirNamedFunctionSymbol)?.name == OperatorNameConventions.INVOKE
) { ) {
val firstArg = candidate.argumentMapping?.keys?.firstOrNull() as? FirThisReceiverExpression ?: return val firstArg = candidate.argumentMapping?.keys?.firstOrNull() as? FirThisReceiverExpression ?: return
@@ -36,7 +36,7 @@ fun extractLambdaInfoFromFunctionalType(
duringCompletion duringCompletion
) )
} }
if (!expectedType.isBuiltinFunctionalType(session)) return null if (!expectedType.isSomeFunctionalType(session)) return null
val singleStatement = argument.body?.statements?.singleOrNull() as? FirReturnExpression val singleStatement = argument.body?.statements?.singleOrNull() as? FirReturnExpression
if (argument.returnType == null && singleStatement != null && if (argument.returnType == null && singleStatement != null &&
@@ -109,7 +109,7 @@ private fun extractLambdaInfo(
val isFunctionSupertype = val isFunctionSupertype =
expectedType != null && expectedType.lowerBoundIfFlexible() expectedType != null && expectedType.lowerBoundIfFlexible()
.isBuiltinFunctionalType(session)//isNotNullOrNullableFunctionSupertype(expectedType) .isSomeFunctionalType(session)//isNotNullOrNullableFunctionSupertype(expectedType)
val typeVariable = ConeTypeVariableForLambdaReturnType(argument, "_L") val typeVariable = ConeTypeVariableForLambdaReturnType(argument, "_L")
@@ -177,7 +177,7 @@ internal fun extractInputOutputTypesFromCallableReferenceExpectedType(
if (expectedType == null) return null if (expectedType == null) return null
return when { return when {
expectedType.isBuiltinFunctionalType(session) -> expectedType.isSomeFunctionalType(session) ->
InputOutputTypes(expectedType.valueParameterTypesIncludingReceiver(session), expectedType.returnType(session)) InputOutputTypes(expectedType.valueParameterTypesIncludingReceiver(session), expectedType.returnType(session))
// ReflectionTypes.isBaseTypeForNumberedReferenceTypes(expectedType) -> // ReflectionTypes.isBaseTypeForNumberedReferenceTypes(expectedType) ->
@@ -561,7 +561,7 @@ class FirCallCompletionResultsWriterTransformer(
// From the argument mapping, the expected type of this anonymous function would be: // From the argument mapping, the expected type of this anonymous function would be:
when { when {
// a built-in functional type, no-brainer // a built-in functional type, no-brainer
expectedArgumentType.isBuiltinFunctionalType(session) -> expectedArgumentType expectedArgumentType.isSomeFunctionalType(session) -> expectedArgumentType
// fun interface (a.k.a. SAM), then unwrap it and build a functional type from that interface function // fun interface (a.k.a. SAM), then unwrap it and build a functional type from that interface function
expectedArgumentType is ConeClassLikeType -> { expectedArgumentType is ConeClassLikeType -> {
val firRegularClass = val firRegularClass =
@@ -19,7 +19,7 @@ import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirWrappedArgumentExpression import org.jetbrains.kotlin.fir.expressions.FirWrappedArgumentExpression
import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isFunctionalOrSuspendFunctionalType import org.jetbrains.kotlin.fir.types.isNonReflectFunctionalType
tailrec fun FirExpression.unwrapAnonymousFunctionExpression(): FirAnonymousFunction? = when (this) { tailrec fun FirExpression.unwrapAnonymousFunctionExpression(): FirAnonymousFunction? = when (this) {
is FirAnonymousFunctionExpression -> anonymousFunction is FirAnonymousFunctionExpression -> anonymousFunction
@@ -47,7 +47,7 @@ fun FirFunctionCall.replaceLambdaArgumentInvocationKinds(session: FirSession) {
val kind = byParameter[parameter] ?: EventOccurrencesRange.UNKNOWN.takeIf { val kind = byParameter[parameter] ?: EventOccurrencesRange.UNKNOWN.takeIf {
// Inline functional parameters have to be called in-place; that's the only permitted operation on them. // Inline functional parameters have to be called in-place; that's the only permitted operation on them.
isInline && !parameter.isNoinline && !parameter.isCrossinline && isInline && !parameter.isNoinline && !parameter.isCrossinline &&
parameter.returnTypeRef.coneType.isFunctionalOrSuspendFunctionalType(session) parameter.returnTypeRef.coneType.isNonReflectFunctionalType(session)
} ?: continue } ?: continue
lambda.replaceInvocationKind(kind) lambda.replaceInvocationKind(kind)
} }
@@ -39,7 +39,6 @@ import org.jetbrains.kotlin.fir.resolve.transformers.FirCallCompletionResultsWri
import org.jetbrains.kotlin.fir.resolve.transformers.FirStatusResolver import org.jetbrains.kotlin.fir.resolve.transformers.FirStatusResolver
import org.jetbrains.kotlin.fir.resolve.transformers.transformVarargTypeToArrayType import org.jetbrains.kotlin.fir.resolve.transformers.transformVarargTypeToArrayType
import org.jetbrains.kotlin.fir.scopes.impl.FirMemberTypeParameterScope import org.jetbrains.kotlin.fir.scopes.impl.FirMemberTypeParameterScope
import org.jetbrains.kotlin.fir.types.constructStarProjectedType
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
@@ -859,7 +858,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
lambda: FirAnonymousFunction lambda: FirAnonymousFunction
): List<FirValueParameter> { ): List<FirValueParameter> {
if (expectedType == null) return lambda.valueParameters if (expectedType == null) return lambda.valueParameters
if (!expectedType.isFunctionalOrSuspendFunctionalType(session)) return lambda.valueParameters if (!expectedType.isNonReflectFunctionalType(session)) return lambda.valueParameters
val parameterTypes = expectedType.typeArguments val parameterTypes = expectedType.typeArguments
.mapTo(mutableListOf()) { it.type ?: session.builtinTypes.nullableAnyType.type } .mapTo(mutableListOf()) { it.type ?: session.builtinTypes.nullableAnyType.type }
.also { it.removeLastOrNull() } .also { it.removeLastOrNull() }
@@ -742,7 +742,13 @@ class FirRenderer(
} }
override fun visitResolvedTypeRef(resolvedTypeRef: FirResolvedTypeRef) { override fun visitResolvedTypeRef(resolvedTypeRef: FirResolvedTypeRef) {
typeRenderer.renderAsPossibleFunctionType(resolvedTypeRef.type) typeRenderer.renderAsPossibleFunctionType(
resolvedTypeRef.type,
l@{
val classId = it.classId ?: return@l null
FirFunctionalTypeKindService.Default.getKindByClassNamePrefix(classId.packageFqName, classId.shortClassName.asString())
}
)
} }
override fun visitUserTypeRef(userTypeRef: FirUserTypeRef) { override fun visitUserTypeRef(userTypeRef: FirUserTypeRef) {
@@ -0,0 +1,32 @@
/*
* 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.
*/
package org.jetbrains.kotlin.fir.types
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.name.FqName
abstract class FirFunctionalTypeKindService : FirSessionComponent {
abstract fun getKindByClassNamePrefix(packageFqName: FqName, className: String): ConeFunctionalTypeKind?
/*
* Should be used only in places where session is unavaliable by default (e.g. in default cone type render)
*/
object Default : FirFunctionalTypeKindService() {
override fun getKindByClassNamePrefix(packageFqName: FqName, className: String): ConeFunctionalTypeKind? {
return when (FunctionClassKind.parseClassName(className, packageFqName)?.kind) {
FunctionClassKind.Function -> ConeFunctionalTypeKind.Function
FunctionClassKind.SuspendFunction -> ConeFunctionalTypeKind.SuspendFunction
FunctionClassKind.KFunction -> ConeFunctionalTypeKind.KFunction
FunctionClassKind.KSuspendFunction -> ConeFunctionalTypeKind.KSuspendFunction
null -> null
}
}
}
}
val FirSession.functionalTypeService: FirFunctionalTypeKindService by FirSession.sessionComponentAccessor()
@@ -337,7 +337,10 @@ class FirVisualizer(private val firFile: FirFile) : BaseRenderer() {
return return
} }
val projectionBuilder = StringBuilder() val projectionBuilder = StringBuilder()
ConeTypeRendererForDebugging(projectionBuilder).renderAsPossibleFunctionType(this) { ConeTypeRendererForDebugging(projectionBuilder).renderAsPossibleFunctionType(
this,
{ it.functionalTypeKind(session) }
) {
localTypeProjectionRenderer(projectionBuilder) localTypeProjectionRenderer(projectionBuilder)
} }
builder.append(projectionBuilder.toString().removeCurrentFilePackage()) builder.append(projectionBuilder.toString().removeCurrentFilePackage())
@@ -114,7 +114,7 @@ object FirParcelizePropertyChecker : FirPropertyChecker() {
type.anySuperTypeConstructor { type.anySuperTypeConstructor {
it is ConeKotlinType && it is ConeKotlinType &&
(it.classId?.asFqNameString() in BuiltinParcelableTypes.PARCELABLE_SUPERTYPE_FQNAMES || (it.classId?.asFqNameString() in BuiltinParcelableTypes.PARCELABLE_SUPERTYPE_FQNAMES ||
it.isBuiltinFunctionalType(session)) it.isSomeFunctionalType(session))
} }
} }
} }