[FIR] Split :compiler:fir:resolve module into three different modules

Those modules are:
- :compiler:fir:providers, which contains Fir and Symbol providers,
    scopes, and different utilities used by them
- :compiler:fir:semantics, which contains different abstractions and
    entities which are used in resolution and in checkers
- :compiler:fir:resolve, which contains all stuff related to resolution
    and inference

There are two pros of this change:
1. It may increase gradle build, because it allows to compile :fir:resolve
  and :fir:checkers modules in parallel
2. Logic of working FIR (scopes, providers, DFA logic system, etc) is
  now separated from logic of resolution phases, so for example checkers,
  which are depend on scopes physically will not be able to run resolve
  in any way
This commit is contained in:
Dmitriy Novozhilov
2021-10-14 13:53:40 +03:00
parent 6995ee9334
commit bee44c6e0f
219 changed files with 1836 additions and 1581 deletions
+20
View File
@@ -0,0 +1,20 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
api(project(":core:compiler.common"))
api(project(":compiler:resolution.common"))
api(project(":compiler:fir:cones"))
api(project(":compiler:fir:tree"))
implementation(project(":core:util.runtime"))
compileOnly(project(":kotlin-reflect-api"))
compileOnly(intellijCoreDep()) { includeJars("guava", rootProject = rootProject) }
}
sourceSets {
"main" { projectDefault() }
"test" { none() }
}
@@ -0,0 +1,217 @@
/*
* Copyright 2010-2021 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
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.buildAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.builder.buildTypeParameter
import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.*
import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.scopes.impl.FirIntegerOperatorCall
import org.jetbrains.kotlin.fir.scopes.impl.FirIntegerOperatorCallBuilder
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
fun FirFunctionCall.copy(
annotations: List<FirAnnotation> = this.annotations,
argumentList: FirArgumentList = this.argumentList,
calleeReference: FirNamedReference = this.calleeReference,
explicitReceiver: FirExpression? = this.explicitReceiver,
dispatchReceiver: FirExpression = this.dispatchReceiver,
extensionReceiver: FirExpression = this.extensionReceiver,
source: FirSourceElement? = this.source,
typeArguments: List<FirTypeProjection> = this.typeArguments,
resultType: FirTypeRef = this.typeRef
): FirFunctionCall {
val builder = if (this is FirIntegerOperatorCall) {
FirIntegerOperatorCallBuilder().apply {
this.calleeReference = calleeReference
}
} else {
FirFunctionCallBuilder().apply {
this.calleeReference = calleeReference
}
}
builder.apply {
this.source = source
this.annotations.addAll(annotations)
this.argumentList = argumentList
this.explicitReceiver = explicitReceiver
this.dispatchReceiver = dispatchReceiver
this.extensionReceiver = extensionReceiver
this.typeArguments.addAll(typeArguments)
this.typeRef = resultType
}
return (builder as FirCallBuilder).build() as FirFunctionCall
}
inline fun FirFunctionCall.copyAsImplicitInvokeCall(
setupCopy: FirImplicitInvokeCallBuilder.() -> Unit
): FirImplicitInvokeCall {
val original = this
return buildImplicitInvokeCall {
source = original.source
annotations.addAll(original.annotations)
typeArguments.addAll(original.typeArguments)
explicitReceiver = original.explicitReceiver
dispatchReceiver = original.dispatchReceiver
extensionReceiver = original.extensionReceiver
argumentList = original.argumentList
calleeReference = original.calleeReference
setupCopy()
}
}
fun FirAnonymousFunction.copy(
receiverTypeRef: FirTypeRef? = this.receiverTypeRef,
source: FirSourceElement? = this.source,
moduleData: FirModuleData = this.moduleData,
origin: FirDeclarationOrigin = this.origin,
returnTypeRef: FirTypeRef = this.returnTypeRef,
valueParameters: List<FirValueParameter> = this.valueParameters,
body: FirBlock? = this.body,
annotations: List<FirAnnotation> = this.annotations,
typeRef: FirTypeRef = this.typeRef,
label: FirLabel? = this.label,
controlFlowGraphReference: FirControlFlowGraphReference? = this.controlFlowGraphReference,
invocationKind: EventOccurrencesRange? = this.invocationKind
): FirAnonymousFunction {
return buildAnonymousFunction {
this.source = source
this.moduleData = moduleData
this.origin = origin
this.returnTypeRef = returnTypeRef
this.receiverTypeRef = receiverTypeRef
symbol = this@copy.symbol
isLambda = this@copy.isLambda
this.valueParameters.addAll(valueParameters)
this.body = body
this.annotations.addAll(annotations)
this.typeRef = typeRef
this.label = label
this.controlFlowGraphReference = controlFlowGraphReference
this.invocationKind = invocationKind
}
}
fun FirTypeRef.resolvedTypeFromPrototype(
type: ConeKotlinType
): FirResolvedTypeRef {
return if (type is ConeKotlinErrorType) {
buildErrorTypeRef {
source = this@resolvedTypeFromPrototype.source
diagnostic = type.diagnostic
}
} else {
buildResolvedTypeRef {
source = this@resolvedTypeFromPrototype.source
this.type = type
annotations += this@resolvedTypeFromPrototype.annotations
}
}
}
fun FirTypeRef.errorTypeFromPrototype(
diagnostic: ConeDiagnostic
): FirErrorTypeRef {
return buildErrorTypeRef {
source = this@errorTypeFromPrototype.source
this.diagnostic = diagnostic
}
}
fun FirTypeParameter.copy(
bounds: List<FirTypeRef> = this.bounds,
annotations: List<FirAnnotation> = this.annotations
): FirTypeParameter {
return buildTypeParameter {
source = this@copy.source
resolvePhase = this@copy.resolvePhase
moduleData = this@copy.moduleData
name = this@copy.name
symbol = this@copy.symbol
variance = this@copy.variance
isReified = this@copy.isReified
this.bounds += bounds
this.annotations += annotations
}
}
fun FirWhenExpression.copy(
resultType: FirTypeRef = this.typeRef,
calleeReference: FirReference = this.calleeReference,
annotations: List<FirAnnotation> = this.annotations
): FirWhenExpression = buildWhenExpression {
source = this@copy.source
subject = this@copy.subject
subjectVariable = this@copy.subjectVariable
this.calleeReference = calleeReference
branches += this@copy.branches
typeRef = resultType
this.annotations += annotations
usedAsExpression = this@copy.usedAsExpression
exhaustivenessStatus = this@copy.exhaustivenessStatus
}
fun FirTryExpression.copy(
resultType: FirTypeRef = this.typeRef,
calleeReference: FirReference = this.calleeReference,
annotations: List<FirAnnotation> = this.annotations
): FirTryExpression = buildTryExpression {
source = this@copy.source
tryBlock = this@copy.tryBlock
finallyBlock = this@copy.finallyBlock
this.calleeReference = calleeReference
catches += this@copy.catches
typeRef = resultType
this.annotations += annotations
}
fun FirCheckNotNullCall.copy(
resultType: FirTypeRef = this.typeRef,
calleeReference: FirReference = this.calleeReference,
annotations: List<FirAnnotation> = this.annotations
): FirCheckNotNullCall = buildCheckNotNullCall {
source = this@copy.source
this.calleeReference = calleeReference
argumentList = this@copy.argumentList
this.typeRef = resultType
this.annotations += annotations
}
fun FirDeclarationStatus.copy(
isExpect: Boolean = this.isExpect,
newModality: Modality? = null,
newVisibility: Visibility? = null,
newEffectiveVisibility: EffectiveVisibility? = null,
isOperator: Boolean = this.isOperator
): FirDeclarationStatus {
return if (this.isExpect == isExpect && newModality == null && newVisibility == null && this.isOperator == isOperator) {
this
} else {
require(this is FirDeclarationStatusImpl) { "Unexpected class ${this::class}" }
this.resolved(
newVisibility ?: visibility,
newModality ?: modality!!,
newEffectiveVisibility ?: EffectiveVisibility.Public
).apply {
this.isExpect = isExpect
this.isOperator = isOperator
}
}
}
@@ -0,0 +1,319 @@
/*
* Copyright 2010-2021 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
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticPropertyAccessor
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.resolve.calls.ExpressionReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticFunctionSymbol
import org.jetbrains.kotlin.fir.resolve.calls.ReceiverValue
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.resolve.typeWithStarProjections
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.typeContext
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.types.AbstractTypeChecker
abstract class FirModuleVisibilityChecker : FirSessionComponent {
abstract fun <T> isInFriendModule(declaration: T): Boolean where T : FirMemberDeclaration, T : FirDeclaration
class Standard(val session: FirSession) : FirModuleVisibilityChecker() {
override fun <T> isInFriendModule(declaration: T): Boolean where T : FirMemberDeclaration, T : FirDeclaration {
val useSiteModuleData = session.moduleData
val declarationModuleData = declaration.moduleData
return useSiteModuleData == declarationModuleData || declarationModuleData in useSiteModuleData.friendDependencies
}
}
}
abstract class FirVisibilityChecker : FirSessionComponent {
@NoMutableState
object Default : FirVisibilityChecker() {
override fun platformVisibilityCheck(
declarationVisibility: Visibility,
symbol: FirBasedSymbol<*>,
useSiteFile: FirFile,
containingDeclarations: List<FirDeclaration>,
dispatchReceiver: ReceiverValue?,
session: FirSession,
isCallToPropertySetter: Boolean,
): Boolean {
return true
}
}
fun isVisible(
declaration: FirMemberDeclaration,
session: FirSession,
useSiteFile: FirFile,
containingDeclarations: List<FirDeclaration>,
dispatchReceiver: ReceiverValue?,
isCallToPropertySetter: Boolean = false,
): Boolean {
require(declaration is FirDeclaration)
val provider = session.firProvider
val symbol = declaration.symbol
return when (declaration.visibility) {
Visibilities.Internal -> {
declaration.moduleData == session.moduleData || session.moduleVisibilityChecker?.isInFriendModule(declaration) == true
}
Visibilities.Private, Visibilities.PrivateToThis -> {
val ownerLookupTag = symbol.getOwnerLookupTag()
if (declaration.moduleData == session.moduleData) {
when {
ownerLookupTag == null -> {
val candidateFile = when (symbol) {
is FirSyntheticFunctionSymbol -> {
// SAM case
val classId = ClassId(symbol.callableId.packageName, symbol.callableId.callableName)
provider.getFirClassifierContainerFile(classId)
}
is FirClassLikeSymbol<*> -> provider.getFirClassifierContainerFileIfAny(symbol)
is FirCallableSymbol<*> -> provider.getFirCallableContainerFile(symbol)
else -> null
}
// Top-level: visible in file
candidateFile == useSiteFile
}
declaration is FirConstructor && declaration.isFromSealedClass -> {
// Sealed class constructor: visible in same package
declaration.symbol.callableId.packageName == useSiteFile.packageFqName
}
else -> {
// Member: visible inside parent class, including all its member classes
canSeePrivateMemberOf(containingDeclarations, ownerLookupTag, session)
}
}
} else {
declaration is FirSimpleFunction && declaration.isAllowedToBeAccessedFromOutside()
}
}
Visibilities.Protected -> {
val ownerId = symbol.getOwnerLookupTag()
ownerId != null && canSeeProtectedMemberOf(
containingDeclarations, dispatchReceiver, ownerId, session,
isVariableOrNamedFunction = symbol is FirVariableSymbol || symbol is FirNamedFunctionSymbol || symbol is FirPropertyAccessorSymbol,
symbol.fir is FirSyntheticPropertyAccessor
)
}
else -> platformVisibilityCheck(
declaration.visibility,
symbol,
useSiteFile,
containingDeclarations,
dispatchReceiver,
session,
isCallToPropertySetter,
)
}
}
protected abstract fun platformVisibilityCheck(
declarationVisibility: Visibility,
symbol: FirBasedSymbol<*>,
useSiteFile: FirFile,
containingDeclarations: List<FirDeclaration>,
dispatchReceiver: ReceiverValue?,
session: FirSession,
isCallToPropertySetter: Boolean,
): Boolean
private fun canSeePrivateMemberOf(
containingDeclarationOfUseSite: List<FirDeclaration>,
ownerLookupTag: ConeClassLikeLookupTag,
session: FirSession
): Boolean {
ownerLookupTag.ownerIfCompanion(session)?.let { companionOwnerLookupTag ->
return canSeePrivateMemberOf(containingDeclarationOfUseSite, companionOwnerLookupTag, session)
}
for (declaration in containingDeclarationOfUseSite) {
if (declaration !is FirClass) continue
val boundSymbol = declaration.symbol
if (boundSymbol.classId.isSame(ownerLookupTag.classId)) {
return true
}
}
return false
}
// 'local' isn't taken into account here
private fun ClassId.isSame(other: ClassId): Boolean =
packageFqName == other.packageFqName && relativeClassName == other.relativeClassName
private fun ConeClassLikeLookupTag.ownerIfCompanion(session: FirSession): ConeClassLikeLookupTag? {
if (classId.isLocal) return null
val outerClassId = classId.outerClassId ?: return null
val ownerSymbol = toSymbol(session) as? FirRegularClassSymbol
if (ownerSymbol?.fir?.isCompanion == true) {
return ConeClassLikeLookupTagImpl(outerClassId)
}
return null
}
private fun canSeeProtectedMemberOf(
containingUseSiteClass: FirClass,
dispatchReceiver: ReceiverValue?,
ownerLookupTag: ConeClassLikeLookupTag,
session: FirSession,
isVariableOrNamedFunction: Boolean,
isSyntheticProperty: Boolean
): Boolean {
dispatchReceiver?.ownerIfCompanion(session)?.let { companionOwnerLookupTag ->
if (containingUseSiteClass.isSubClass(companionOwnerLookupTag, session)) return true
}
return when {
!containingUseSiteClass.isSubClass(ownerLookupTag, session) -> false
isVariableOrNamedFunction -> doesReceiverFitForProtectedVisibility(
dispatchReceiver,
containingUseSiteClass,
ownerLookupTag,
isSyntheticProperty,
session
)
else -> true
}
}
private fun doesReceiverFitForProtectedVisibility(
dispatchReceiver: ReceiverValue?,
containingUseSiteClass: FirClass,
ownerLookupTag: ConeClassLikeLookupTag,
isSyntheticProperty: Boolean,
session: FirSession
): Boolean {
if (dispatchReceiver == null) return true
var dispatchReceiverType = dispatchReceiver.type
if (dispatchReceiver is ExpressionReceiverValue) {
val explicitReceiver = dispatchReceiver.explicitReceiver
if (explicitReceiver is FirPropertyAccessExpression && explicitReceiver.calleeReference is FirSuperReference) {
// Special 'super' case: type of this, not of super, should be taken for the check below
dispatchReceiverType = explicitReceiver.dispatchReceiver.typeRef.coneType
}
}
val typeCheckerState = session.typeContext.newTypeCheckerState(
errorTypesEqualToAnything = false,
stubTypesEqualToAnything = false
)
if (AbstractTypeChecker.isSubtypeOf(
typeCheckerState, dispatchReceiverType.fullyExpandedType(session), containingUseSiteClass.typeWithStarProjections()
)
) {
return true
}
if (isSyntheticProperty) {
return if (session.languageVersionSettings.supportsFeature(LanguageFeature.ImproveReportingDiagnosticsOnProtectedMembersOfBaseClass))
containingUseSiteClass.classId.packageFqName == ownerLookupTag.classId.packageFqName
else
true
}
return false
}
private fun FirClass.isSubClass(ownerLookupTag: ConeClassLikeLookupTag, session: FirSession): Boolean {
if (classId.isSame(ownerLookupTag.classId)) return true
return lookupSuperTypes(this, lookupInterfaces = true, deep = true, session).any { superType ->
(superType as? ConeClassLikeType)?.fullyExpandedType(session)?.lookupTag?.classId?.isSame(ownerLookupTag.classId) == true
}
}
private fun ReceiverValue?.ownerIfCompanion(session: FirSession): ConeClassLikeLookupTag? =
(this?.type as? ConeClassLikeType)?.lookupTag?.ownerIfCompanion(session)
// monitorEnter/monitorExit are the only functions which are accessed "illegally" (see kotlin/util/Synchronized.kt).
// Since they are intrinsified in the codegen, FIR should treat it as visible.
private fun FirSimpleFunction.isAllowedToBeAccessedFromOutside(): Boolean {
if (!isFromLibrary) return false
val packageName = symbol.callableId.packageName.asString()
val name = name.asString()
return packageName == "kotlin.jvm.internal.unsafe" &&
(name == "monitorEnter" || name == "monitorExit")
}
protected fun canSeeProtectedMemberOf(
containingDeclarationOfUseSite: List<FirDeclaration>,
dispatchReceiver: ReceiverValue?,
ownerLookupTag: ConeClassLikeLookupTag,
session: FirSession,
isVariableOrNamedFunction: Boolean,
isSyntheticProperty: Boolean
): Boolean {
if (canSeePrivateMemberOf(containingDeclarationOfUseSite, ownerLookupTag, session)) return true
for (containingDeclaration in containingDeclarationOfUseSite) {
if (containingDeclaration is FirClass) {
val boundSymbol = containingDeclaration.symbol
if (canSeeProtectedMemberOf(
boundSymbol.fir,
dispatchReceiver,
ownerLookupTag,
session,
isVariableOrNamedFunction,
isSyntheticProperty
)
) return true
} else if (containingDeclaration is FirFile) {
if (isSyntheticProperty &&
session.languageVersionSettings.supportsFeature(LanguageFeature.ImproveReportingDiagnosticsOnProtectedMembersOfBaseClass) &&
containingDeclaration.packageFqName == ownerLookupTag.classId.packageFqName
) {
return true
}
}
}
return false
}
protected fun FirBasedSymbol<*>.packageFqName(): FqName {
return when (this) {
is FirClassLikeSymbol<*> -> classId.packageFqName
is FirCallableSymbol<*> -> callableId.packageName
else -> error("No package fq name for $this")
}
}
}
val FirSession.moduleVisibilityChecker: FirModuleVisibilityChecker? by FirSession.nullableSessionComponentAccessor()
val FirSession.visibilityChecker: FirVisibilityChecker by FirSession.sessionComponentAccessor()
fun FirBasedSymbol<*>.getOwnerLookupTag(): ConeClassLikeLookupTag? {
return when (this) {
is FirBackingFieldSymbol -> fir.propertySymbol.getOwnerLookupTag()
is FirClassLikeSymbol<*> -> {
if (classId.isLocal) {
(fir as? FirRegularClass)?.containingClassForLocal()
} else {
val ownerId = classId.outerClassId
ownerId?.let { ConeClassLikeLookupTagImpl(it) }
}
}
is FirCallableSymbol<*> -> containingClass()
else -> error("Unsupported owner search for ${fir.javaClass}: ${fir.render()}")
}
}
@@ -0,0 +1,163 @@
/*
* Copyright 2010-2021 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.declarations
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
private fun FirAnnotation.toAnnotationLookupTag(): ConeClassLikeLookupTag? =
// this cast fails when we have generic-typed annotations @T
(annotationTypeRef.coneType as? ConeClassLikeType)?.lookupTag
fun FirAnnotation.toAnnotationClassId(): ClassId? =
toAnnotationLookupTag()?.classId
private fun FirAnnotation.toAnnotationClass(session: FirSession): FirRegularClass? =
toAnnotationLookupTag()?.toSymbol(session)?.fir as? FirRegularClass
// TODO: this is temporary solution, we need something better
private val FirExpression.callableNameOfMetaAnnotationArgument: Name?
get() =
(this as? FirQualifiedAccessExpression)?.let {
val callableSymbol = (it.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirCallableSymbol<*>
callableSymbol?.callableId?.callableName
}
private val sourceName = Name.identifier("SOURCE")
fun FirAnnotationContainer.nonSourceAnnotations(session: FirSession): List<FirAnnotation> =
annotations.filter { annotation ->
val firAnnotationClass = annotation.toAnnotationClass(session)
firAnnotationClass != null && firAnnotationClass.annotations.none { meta ->
meta.toAnnotationClassId() == StandardClassIds.Annotations.Retention &&
meta.findArgumentByName(StandardClassIds.Annotations.ParameterNames.retentionValue)
?.callableNameOfMetaAnnotationArgument == sourceName
}
}
inline val FirProperty.hasJvmFieldAnnotation: Boolean
get() = annotations.any { it.isJvmFieldAnnotation }
val FirAnnotation.isJvmFieldAnnotation: Boolean
get() = toAnnotationClassId() == StandardClassIds.Annotations.JvmField
fun FirAnnotation.useSiteTargetsFromMetaAnnotation(session: FirSession): Set<AnnotationUseSiteTarget> {
return toAnnotationClass(session)
?.annotations
?.find { it.toAnnotationClassId() == StandardClassIds.Annotations.Target }
?.findArgumentByName(StandardClassIds.Annotations.ParameterNames.targetAllowedTargets)
?.unwrapVarargValue()
?.toAnnotationUseSiteTargets()
?: DEFAULT_USE_SITE_TARGETS
}
private fun List<FirExpression>.toAnnotationUseSiteTargets(): Set<AnnotationUseSiteTarget> =
flatMapTo(mutableSetOf()) { arg ->
when (val unwrappedArg = if (arg is FirNamedArgumentExpression) arg.expression else arg) {
is FirArrayOfCall -> unwrappedArg.argumentList.arguments.toAnnotationUseSiteTargets()
is FirVarargArgumentsExpression -> unwrappedArg.arguments.toAnnotationUseSiteTargets()
else -> USE_SITE_TARGET_NAME_MAP[unwrappedArg.callableNameOfMetaAnnotationArgument?.identifier] ?: setOf()
}
}
// See [org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.USE_SITE_MAPPING] (it's in reverse)
private val USE_SITE_TARGET_NAME_MAP = mapOf(
"FIELD" to setOf(AnnotationUseSiteTarget.FIELD, AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD),
"FILE" to setOf(AnnotationUseSiteTarget.FILE),
"PROPERTY" to setOf(AnnotationUseSiteTarget.PROPERTY),
"PROPERTY_GETTER" to setOf(AnnotationUseSiteTarget.PROPERTY_GETTER),
"PROPERTY_SETTER" to setOf(AnnotationUseSiteTarget.PROPERTY_SETTER),
"VALUE_PARAMETER" to setOf(
AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER,
AnnotationUseSiteTarget.RECEIVER,
AnnotationUseSiteTarget.SETTER_PARAMETER,
),
)
// See [org.jetbrains.kotlin.descriptors.annotations.KotlinTarget] (the second argument of each entry)
private val DEFAULT_USE_SITE_TARGETS: Set<AnnotationUseSiteTarget> =
USE_SITE_TARGET_NAME_MAP.values.fold(setOf<AnnotationUseSiteTarget>()) { a, b -> a + b } - setOf(AnnotationUseSiteTarget.FILE)
fun FirAnnotatedDeclaration.hasAnnotation(classId: ClassId): Boolean {
return annotations.any { it.toAnnotationClassId() == classId }
}
fun <D> FirBasedSymbol<out D>.getAnnotationByClassId(classId: ClassId): FirAnnotation? where D : FirAnnotationContainer, D : FirDeclaration {
return fir.getAnnotationByClassId(classId)
}
fun FirAnnotationContainer.getAnnotationByClassId(classId: ClassId): FirAnnotation? {
return annotations.find {
it.annotationTypeRef.coneTypeSafe<ConeClassLikeType>()?.lookupTag?.classId == classId
}
}
fun FirAnnotationContainer.getAnnotationsByClassId(classId: ClassId): List<FirAnnotation> = annotations.getAnnotationsByClassId(classId)
fun List<FirAnnotation>.getAnnotationsByClassId(classId: ClassId): List<FirAnnotation> {
return filter {
it.annotationTypeRef.coneTypeSafe<ConeClassLikeType>()?.lookupTag?.classId == classId
}
}
fun FirExpression.unwrapVarargValue(): List<FirExpression> {
return when (this) {
is FirVarargArgumentsExpression -> arguments
is FirArrayOfCall -> arguments
else -> listOf(this)
}
}
fun FirAnnotation.findArgumentByName(name: Name): FirExpression? {
argumentMapping.mapping[name]?.let { return it }
if (this !is FirAnnotationCall) return null
// NB: we have to consider both cases, because deserializer does not create argument mapping
for (argument in arguments) {
if (argument is FirNamedArgumentExpression && argument.name == name) {
return argument.expression
}
}
// I'm lucky today!
// TODO: this line is still needed. However it should be replaced with 'return null'
return arguments.singleOrNull()
}
fun FirAnnotation.getStringArgument(name: Name): String? =
findArgumentByName(name)?.let { expression ->
expression.safeAs<FirConstExpression<*>>()?.value as? String
}
fun FirAnnotationContainer.getJvmNameFromAnnotation(target: AnnotationUseSiteTarget? = null): String? {
val annotationCalls = getAnnotationsByClassId(StandardClassIds.Annotations.JvmName)
return annotationCalls.firstNotNullOfOrNull { call ->
call.getStringArgument(StandardClassIds.Annotations.ParameterNames.jvmNameName)
?.takeIf { target == null || call.useSiteTarget == target }
}
}
val FirAnnotation.resolved: Boolean
get() {
if (annotationTypeRef !is FirResolvedTypeRef) return false
if (this !is FirAnnotationCall) return true
return calleeReference is FirResolvedNamedReference || calleeReference is FirErrorNamedReference
}
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2021 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.declarations
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirArgumentList
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
fun FirElement.validate() {
accept(FirGeneratedElementsValidator, null)
}
/*
* TODO's:
* - add proper error messages to all `require`
* - add validation of declaration origin and resolve phase for all declarations
*/
object FirGeneratedElementsValidator : FirDefaultVisitor<Unit, Any?>() {
override fun visitElement(element: FirElement, data: Any?) {
element.acceptChildren(this, null)
}
override fun visitAnnotation(annotation: FirAnnotation, data: Any?) {
annotation.acceptChildren(this, null)
}
override fun visitAnnotationCall(annotationCall: FirAnnotationCall, data: Any?) {
annotationCall.acceptChildren(this, null)
}
override fun visitRegularClass(regularClass: FirRegularClass, data: Any?) {
regularClass.acceptChildren(this, null)
}
override fun visitArgumentList(argumentList: FirArgumentList, data: Any?) {
require(argumentList is FirResolvedArgumentList)
argumentList.acceptChildren(this, null)
}
override fun visitNamedReference(namedReference: FirNamedReference, data: Any?) {
require(namedReference is FirResolvedNamedReference)
namedReference.acceptChildren(this, null)
}
override fun visitTypeRef(typeRef: FirTypeRef, data: Any?) {
require(typeRef is FirResolvedTypeRef)
typeRef.acceptChildren(this, null)
}
override fun visitResolvedTypeRef(resolvedTypeRef: FirResolvedTypeRef, data: Any?) {
resolvedTypeRef.annotations.forEach { it.accept(this, null) }
}
override fun visitDeclarationStatus(declarationStatus: FirDeclarationStatus, data: Any?) {
require(declarationStatus is FirResolvedDeclarationStatus)
}
override fun visitTypeParameterRef(typeParameterRef: FirTypeParameterRef, data: Any?) {
typeParameterRef.symbol.fir.accept(this, null)
}
}
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2021 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.declarations
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.utils.isInline
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.substitution.createTypeSubstitutorByTypeConstructor
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeLookupTagBasedType
import org.jetbrains.kotlin.fir.types.ConeTypeContext
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.types.model.typeConstructor
internal fun ConeKotlinType.substitutedUnderlyingTypeForInlineClass(session: FirSession, context: ConeTypeContext): ConeKotlinType? {
val unsubstitutedType = unsubstitutedUnderlyingTypeForInlineClass(session) ?: return null
val substitutor = createTypeSubstitutorByTypeConstructor(mapOf(this.typeConstructor(context) to this), context)
return substitutor.substituteOrNull(unsubstitutedType)
}
internal fun ConeKotlinType.unsubstitutedUnderlyingTypeForInlineClass(session: FirSession): ConeKotlinType? {
val symbol = (this.fullyExpandedType(session) as? ConeLookupTagBasedType)
?.lookupTag
?.toSymbol(session) as? FirRegularClassSymbol
?: return null
symbol.ensureResolved(FirResolvePhase.STATUS)
return symbol.fir.getInlineClassUnderlyingParameter(session)?.returnTypeRef?.coneType
}
// TODO: implement inlineClassRepresentation in FirRegularClass instead.
fun FirRegularClass.getInlineClassUnderlyingParameter(session: FirSession): FirValueParameter? =
if (isInline) primaryConstructorIfAny(session)?.fir?.valueParameters?.singleOrNull() else null
@@ -0,0 +1,48 @@
/*
* Copyright 2010-2021 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.declarations
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.utils.addToStdlib.runIf
fun FirClass.constructors(session: FirSession): List<FirConstructorSymbol> {
val result = mutableListOf<FirConstructorSymbol>()
session.declaredMemberScope(this).processDeclaredConstructors { result += it }
return result
}
fun FirClass.constructorsSortedByDelegation(session: FirSession): List<FirConstructorSymbol> {
return constructors(session).sortedWith(ConstructorDelegationComparator)
}
fun FirClass.primaryConstructorIfAny(session: FirSession): FirConstructorSymbol? {
return constructors(session).find(FirConstructorSymbol::isPrimary)
}
fun FirRegularClass.collectEnumEntries(): Collection<FirEnumEntry> {
assert(classKind == ClassKind.ENUM_CLASS)
return declarations.filterIsInstance<FirEnumEntry>()
}
val FirConstructorSymbol.delegatedThisConstructor: FirConstructorSymbol?
get() = runIf(delegatedConstructorCallIsThis) { this.resolvedDelegatedConstructor }
private object ConstructorDelegationComparator : Comparator<FirConstructorSymbol> {
override fun compare(p0: FirConstructorSymbol?, p1: FirConstructorSymbol?): Int {
if (p0 == null && p1 == null) return 0
if (p0 == null) return -1
if (p1 == null) return 1
if (p0.delegatedThisConstructor == p1) return 1
if (p1.delegatedThisConstructor == p0) return -1
// If neither is a delegation to each other, the order doesn't matter.
// Here we return 0 to preserve the original order.
return 0
}
}
@@ -0,0 +1,146 @@
/*
* Copyright 2010-2021 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.declarations
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.ParameterNames
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.ParameterNames.deprecatedSinceKotlinErrorSince
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.ParameterNames.deprecatedSinceKotlinHiddenSince
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.ParameterNames.deprecatedSinceKotlinWarningSince
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue
import org.jetbrains.kotlin.resolve.deprecation.SimpleDeprecationInfo
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
private val JAVA_ORIGINS = setOf(FirDeclarationOrigin.Java, FirDeclarationOrigin.Enhancement)
fun FirBasedSymbol<*>.getDeprecation(callSite: FirElement?): DeprecationInfo? {
val deprecationInfos = mutableListOf<DeprecationInfo>()
when (this) {
is FirPropertySymbol ->
when (callSite) {
is FirVariableAssignment ->
deprecationInfos.addIfNotNull(
getDeprecationForCallSite(AnnotationUseSiteTarget.PROPERTY_SETTER, AnnotationUseSiteTarget.PROPERTY)
)
is FirPropertyAccessExpression ->
deprecationInfos.addIfNotNull(
getDeprecationForCallSite(AnnotationUseSiteTarget.PROPERTY_GETTER, AnnotationUseSiteTarget.PROPERTY)
)
else -> deprecationInfos.addIfNotNull(getDeprecationForCallSite(AnnotationUseSiteTarget.PROPERTY))
}
else -> deprecationInfos.addIfNotNull(getDeprecationForCallSite())
}
return deprecationInfos.firstOrNull()
}
fun FirAnnotationContainer.getDeprecationInfos(currentVersion: ApiVersion): DeprecationsPerUseSite {
val deprecationByUseSite = mutableMapOf<AnnotationUseSiteTarget?, DeprecationInfo>()
val fromJava = JAVA_ORIGINS.contains(this.safeAs<FirDeclaration>()?.origin)
annotations.extractDeprecationInfoPerUseSite(currentVersion, fromJava).toMap(deprecationByUseSite)
if (this is FirProperty) {
getDeprecationsFromAccessors(getter, setter, currentVersion).bySpecificSite?.forEach { (k, v) -> deprecationByUseSite[k] = v }
}
return DeprecationsPerUseSite.fromMap(deprecationByUseSite)
}
@OptIn(ExperimentalStdlibApi::class)
fun getDeprecationsFromAccessors(
getter: FirFunction?,
setter: FirFunction?,
currentVersion: ApiVersion
): DeprecationsPerUseSite {
val perUseSite = buildMap<AnnotationUseSiteTarget, DeprecationInfo> {
val setterDeprecations = setter?.getDeprecationInfos(currentVersion)
setterDeprecations?.all?.let { put(AnnotationUseSiteTarget.PROPERTY_SETTER, it) }
setterDeprecations?.bySpecificSite?.let { putAll(it) }
val getterDeprecations = getter?.getDeprecationInfos(currentVersion)
getterDeprecations?.all?.let { put(AnnotationUseSiteTarget.PROPERTY_GETTER, it) }
getterDeprecations?.bySpecificSite?.let { putAll(it) }
}
return if (perUseSite.isEmpty()) EmptyDeprecationsPerUseSite else DeprecationsPerUseSite(null, perUseSite)
}
fun List<FirAnnotation>.getDeprecationInfosFromAnnotations(currentVersion: ApiVersion, fromJava: Boolean): DeprecationsPerUseSite {
val deprecationByUseSite = extractDeprecationInfoPerUseSite(currentVersion, fromJava).toMap()
return DeprecationsPerUseSite.fromMap(deprecationByUseSite)
}
fun FirBasedSymbol<*>.getDeprecationForCallSite(
vararg sites: AnnotationUseSiteTarget
): DeprecationInfo? {
val deprecations = when (this) {
is FirCallableSymbol<*> -> deprecation
is FirClassLikeSymbol<*> -> deprecation
else -> null
}
return (deprecations ?: EmptyDeprecationsPerUseSite).forUseSite(*sites)
}
private fun FirAnnotation.getVersionFromArgument(name: Name): ApiVersion? =
getStringArgument(name)?.let { ApiVersion.parse(it) }
private fun FirAnnotation.getDeprecationLevel(): DeprecationLevelValue? {
//take last because Annotation might be not resolved yet and arguments passed without explicit names
val argument = if (resolved) {
argumentMapping.mapping[ParameterNames.deprecatedLevel]
} else {
val call = this as? FirAnnotationCall ?: return null
call.arguments
.firstOrNull { it is FirNamedArgumentExpression && it.name == ParameterNames.deprecatedLevel }
?.unwrapArgument()
?: arguments.lastOrNull()
} ?: return null
val targetExpression = argument as? FirQualifiedAccessExpression ?: return null
val targetName = (targetExpression.calleeReference as? FirNamedReference)?.name?.asString() ?: return null
return DeprecationLevelValue.values().find { it.name == targetName }
}
private fun List<FirAnnotation>.extractDeprecationInfoPerUseSite(
currentVersion: ApiVersion,
fromJava: Boolean
): List<Pair<AnnotationUseSiteTarget?, DeprecationInfo>> {
val annotations = getAnnotationsByClassId(StandardClassIds.Annotations.Deprecated).map { it to false } +
getAnnotationsByClassId(StandardClassIds.Annotations.Java.Deprecated).map { it to true }
return annotations.mapNotNull { (deprecated, fromJavaAnnotation) ->
val deprecationLevel = deprecated.getDeprecationLevel() ?: DeprecationLevelValue.WARNING
val deprecatedSinceKotlin = getAnnotationsByClassId(StandardClassIds.Annotations.DeprecatedSinceKotlin).firstOrNull()
fun levelApplied(name: Name, level: DeprecationLevelValue): DeprecationLevelValue? {
deprecatedSinceKotlin?.getVersionFromArgument(name)?.takeIf { it <= currentVersion }?.let { return level }
return level.takeIf { deprecatedSinceKotlin == null && level == deprecationLevel }
}
val appliedLevel = (levelApplied(deprecatedSinceKotlinHiddenSince, DeprecationLevelValue.HIDDEN)
?: levelApplied(deprecatedSinceKotlinErrorSince, DeprecationLevelValue.ERROR)
?: levelApplied(deprecatedSinceKotlinWarningSince, DeprecationLevelValue.WARNING))
appliedLevel?.let {
val inheritable = !fromJavaAnnotation && !fromJava
deprecated.useSiteTarget to SimpleDeprecationInfo(
it,
inheritable,
deprecated.getStringArgument(ParameterNames.deprecatedMessage)
)
}
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2021 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.extensions
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import kotlin.reflect.KClass
/*
* TODO:
* - check that annotations or meta-annotations is not empty
*/
abstract class FirDeclarationGenerationExtension(session: FirSession) : FirPredicateBasedExtension(session) {
companion object {
val NAME = FirExtensionPointName("ExistingClassModification")
}
final override val name: FirExtensionPointName
get() = NAME
final override val extensionType: KClass<out FirExtension> = FirDeclarationGenerationExtension::class
abstract fun needToGenerateAdditionalMembersInClass(klass: FirClass): Boolean
abstract fun needToGenerateNestedClassifiersInClass(klass: FirClass): Boolean
// Can be called on SUPERTYPES stage
open fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? = null
// Can be called on STATUS stage
open fun generateFunctions(callableId: CallableId, owner: FirClassSymbol<*>?): List<FirNamedFunctionSymbol> = emptyList()
open fun generateProperties(callableId: CallableId, owner: FirClassSymbol<*>?): List<FirPropertySymbol> = emptyList()
open fun generateConstructors(callableId: CallableId): List<FirConstructorSymbol> = emptyList()
// Can be called on IMPORTS stage
open fun hasPackage(packageFqName: FqName): Boolean = false
// Can be called after BODY_RESOLVE stage (checkers and fir2ir)
open fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set<Name> = emptySet()
open fun getNestedClassifiersNames(classSymbol: FirClassSymbol<*>): Set<Name> = emptySet()
open fun getTopLevelCallableIds(): Set<CallableId> = emptySet()
open fun getTopLevelClassIds(): Set<ClassId> = emptySet()
fun interface Factory : FirExtension.Factory<FirDeclarationGenerationExtension>
}
val FirExtensionService.declarationGenerators: List<FirDeclarationGenerationExtension> by FirExtensionService.registeredExtensions()
@@ -0,0 +1,112 @@
/*
* Copyright 2010-2021 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.resolve
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.declarations.getAnnotationsByClassId
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotation
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationArgumentMapping
import org.jetbrains.kotlin.fir.expressions.builder.buildConstExpression
import org.jetbrains.kotlin.fir.fakeElement
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTagWithFixedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.utils.WeakPair
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.types.ConstantValueKind
fun ConeClassifierLookupTag.toSymbol(useSiteSession: FirSession): FirClassifierSymbol<*>? =
when (this) {
is ConeClassLikeLookupTag -> toSymbol(useSiteSession)
is ConeClassifierLookupTagWithFixedSymbol -> this.symbol
else -> null
}
@OptIn(LookupTagInternals::class)
fun ConeClassLikeLookupTag.toSymbol(useSiteSession: FirSession): FirClassLikeSymbol<*>? {
if (this is ConeClassLookupTagWithFixedSymbol) {
return this.symbol
}
val firSymbolProvider = useSiteSession.symbolProvider
(this as? ConeClassLikeLookupTagImpl)?.boundSymbol?.takeIf { it.first === useSiteSession }?.let { return it.second }
return firSymbolProvider.getClassLikeSymbolByClassId(classId).also {
(this as? ConeClassLikeLookupTagImpl)?.bindSymbolToLookupTag(useSiteSession, it)
}
}
@OptIn(LookupTagInternals::class)
fun ConeClassLikeLookupTag.toSymbolOrError(useSiteSession: FirSession): FirClassLikeSymbol<*> =
toSymbol(useSiteSession)
?: error("Class symbol with classId $classId was not found")
@OptIn(LookupTagInternals::class)
fun ConeClassLikeLookupTag.toFirRegularClassSymbol(session: FirSession): FirRegularClassSymbol? =
session.symbolProvider.getSymbolByLookupTag(this) as? FirRegularClassSymbol
@OptIn(LookupTagInternals::class)
fun ConeClassLikeLookupTagImpl.bindSymbolToLookupTag(session: FirSession, symbol: FirClassLikeSymbol<*>?) {
boundSymbol = WeakPair(session, symbol)
}
@LookupTagInternals
fun ConeClassLikeLookupTag.toFirRegularClass(session: FirSession): FirRegularClass? =
session.symbolProvider.getSymbolByLookupTag(this)?.fir as? FirRegularClass
fun FirSymbolProvider.getSymbolByLookupTag(lookupTag: ConeClassifierLookupTag): FirClassifierSymbol<*>? {
return lookupTag.toSymbol(session)
}
fun FirSymbolProvider.getSymbolByLookupTag(lookupTag: ConeClassLikeLookupTag): FirClassLikeSymbol<*>? {
return lookupTag.toSymbol(session)
}
fun ConeKotlinType.withParameterNameAnnotation(valueParameter: FirValueParameter, context: ConeTypeContext): ConeKotlinType {
if (valueParameter.name == SpecialNames.NO_NAME_PROVIDED || valueParameter.name == SpecialNames.UNDERSCORE_FOR_UNUSED_VAR) return this
// Existing @ParameterName annotation takes precedence
if (attributes.customAnnotations.getAnnotationsByClassId(StandardNames.FqNames.parameterNameClassId).isNotEmpty()) return this
val fakeSource = valueParameter.source?.fakeElement(FirFakeSourceElementKind.ParameterNameAnnotationCall)
val parameterNameAnnotationCall = buildAnnotation {
source = fakeSource
annotationTypeRef =
buildResolvedTypeRef {
source = fakeSource
type = ConeClassLikeTypeImpl(
ConeClassLikeLookupTagImpl(StandardNames.FqNames.parameterNameClassId),
emptyArray(),
isNullable = false
)
}
argumentMapping = buildAnnotationArgumentMapping {
mapping[StandardClassIds.Annotations.ParameterNames.parameterNameName] =
buildConstExpression(fakeSource, ConstantValueKind.String, valueParameter.name.asString(), setType = true)
}
}
val attributesWithParameterNameAnnotation =
ConeAttributes.create(listOf(CustomAnnotationTypeAttribute(listOf(parameterNameAnnotationCall))))
return withCombinedCustomAttributesFrom(attributesWithParameterNameAnnotation, context)
}
fun ConeKotlinType.withCombinedCustomAttributesFrom(other: ConeKotlinType, context: ConeTypeContext): ConeKotlinType =
withCombinedCustomAttributesFrom(other.attributes, context)
private fun ConeKotlinType.withCombinedCustomAttributesFrom(other: ConeAttributes, context: ConeTypeContext): ConeKotlinType {
val customAttributesFromOther = other.custom ?: return this
val combinedConeAttributes = attributes.add(ConeAttributes.create(listOf(customAttributesFromOther)))
return withAttributes(combinedConeAttributes, context)
}
@@ -0,0 +1,131 @@
/*
* Copyright 2010-2021 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.resolve
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.expressions.FirExpressionWithSmartcast
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcastToNull
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.FirUnstableSmartcastTypeScope
import org.jetbrains.kotlin.fir.scopes.impl.FirScopeWithFakeOverrideTypeCalculator
import org.jetbrains.kotlin.fir.scopes.impl.FirStandardOverrideChecker
import org.jetbrains.kotlin.fir.scopes.impl.FirTypeIntersectionScope
import org.jetbrains.kotlin.fir.scopes.scopeForClass
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.name.ClassId
fun FirExpressionWithSmartcast.smartcastScope(
useSiteSession: FirSession,
scopeSession: ScopeSession
): FirTypeScope? {
val smartcastType =
if (this is FirExpressionWithSmartcastToNull) smartcastTypeWithoutNullableNothing.coneType else smartcastType.coneType
val smartcastScope = smartcastType.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing)
if (isStable) {
return smartcastScope
}
val originalScope = originalType.coneType.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing)
?: return smartcastScope
if (smartcastScope == null) {
return originalScope
}
return FirUnstableSmartcastTypeScope(smartcastScope, originalScope)
}
fun ConeKotlinType.scope(
useSiteSession: FirSession,
scopeSession: ScopeSession,
fakeOverrideTypeCalculator: FakeOverrideTypeCalculator
): FirTypeScope? {
val scope = scope(useSiteSession, scopeSession, FirResolvePhase.DECLARATIONS) ?: return null
if (fakeOverrideTypeCalculator == FakeOverrideTypeCalculator.DoNothing) return scope
return FirScopeWithFakeOverrideTypeCalculator(scope, fakeOverrideTypeCalculator)
}
private fun ConeKotlinType.scope(useSiteSession: FirSession, scopeSession: ScopeSession, requiredPhase: FirResolvePhase): FirTypeScope? {
return when (this) {
is ConeKotlinErrorType -> null
is ConeClassLikeType -> {
val fullyExpandedType = fullyExpandedType(useSiteSession)
val fir = fullyExpandedType.lookupTag.toSymbol(useSiteSession)?.fir as? FirClass ?: return null
fir.symbol.ensureResolved(requiredPhase)
val substitution = createSubstitution(fir.typeParameters, fullyExpandedType, useSiteSession)
fir.scopeForClass(substitutorByMap(substitution, useSiteSession), useSiteSession, scopeSession)
}
is ConeTypeParameterType -> {
val symbol = lookupTag.symbol
scopeSession.getOrBuild(symbol, TYPE_PARAMETER_SCOPE_KEY) {
val intersectionType = ConeTypeIntersector.intersectTypes(
useSiteSession.typeContext,
symbol.fir.bounds.map { it.coneType }
)
intersectionType.scope(useSiteSession, scopeSession, requiredPhase) ?: FirTypeScope.Empty
}
}
is ConeRawType -> lowerBound.scope(useSiteSession, scopeSession, requiredPhase)
is ConeFlexibleType -> lowerBound.scope(useSiteSession, scopeSession, requiredPhase)
is ConeIntersectionType -> FirTypeIntersectionScope.prepareIntersectionScope(
useSiteSession,
FirStandardOverrideChecker(useSiteSession),
intersectedTypes.mapNotNullTo(mutableListOf()) {
it.scope(useSiteSession, scopeSession, requiredPhase)
},
type
)
is ConeDefinitelyNotNullType -> original.scope(useSiteSession, scopeSession, requiredPhase)
is ConeIntegerLiteralType -> error("ILT should not be in receiver position")
else -> null
}
}
fun FirClassSymbol<*>.defaultType(): ConeClassLikeType = fir.defaultType()
fun FirClass.defaultType(): ConeClassLikeType =
ConeClassLikeTypeImpl(
symbol.toLookupTag(),
typeParameters.map {
ConeTypeParameterTypeImpl(
it.symbol.toLookupTag(),
isNullable = false
)
}.toTypedArray(),
isNullable = false
)
fun ClassId.defaultType(parameters: List<FirTypeParameterSymbol>): ConeClassLikeType =
ConeClassLikeTypeImpl(
ConeClassLikeLookupTagImpl(this),
parameters.map {
ConeTypeParameterTypeImpl(
it.toLookupTag(),
isNullable = false
)
}.toTypedArray(),
isNullable = false,
)
fun FirClass.typeWithStarProjections(): ConeClassLikeType =
ConeClassLikeTypeImpl(
symbol.toLookupTag(),
typeParameters.map { ConeStarProjection }.toTypedArray(),
isNullable = false
)
val TYPE_PARAMETER_SCOPE_KEY = scopeSessionKey<FirTypeParameterSymbol, FirTypeScope>()
@@ -0,0 +1,219 @@
/*
* Copyright 2010-2021 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.resolve
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.types.model.CaptureStatus
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.SmartSet
abstract class SupertypeSupplier {
abstract fun forClass(firClass: FirClass, useSiteSession: FirSession): List<ConeClassLikeType>
abstract fun expansionForTypeAlias(typeAlias: FirTypeAlias, useSiteSession: FirSession): ConeClassLikeType?
object Default : SupertypeSupplier() {
override fun forClass(firClass: FirClass, useSiteSession: FirSession): List<ConeClassLikeType> {
if (!firClass.isLocal) {
// for local classes the phase may not be updated till that moment
firClass.ensureResolved(FirResolvePhase.SUPER_TYPES)
}
return firClass.superConeTypes
}
override fun expansionForTypeAlias(typeAlias: FirTypeAlias, useSiteSession: FirSession): ConeClassLikeType? {
typeAlias.ensureResolved(FirResolvePhase.SUPER_TYPES)
return typeAlias.expandedConeType
}
}
}
fun lookupSuperTypes(
klass: FirClass,
lookupInterfaces: Boolean,
deep: Boolean,
useSiteSession: FirSession,
supertypeSupplier: SupertypeSupplier = SupertypeSupplier.Default,
substituteTypes: Boolean = false
): List<ConeClassLikeType> {
return SmartList<ConeClassLikeType>().also {
klass.symbol.collectSuperTypes(it, SmartSet.create(), deep, lookupInterfaces, substituteTypes, useSiteSession, supertypeSupplier)
}
}
fun FirClass.isThereLoopInSupertypes(session: FirSession): Boolean {
val visitedSymbols: MutableSet<FirClassifierSymbol<*>> = SmartSet.create()
val inProcess: MutableSet<FirClassifierSymbol<*>> = mutableSetOf()
var isThereLoop = false
fun dfs(current: FirClassifierSymbol<*>) {
if (current in visitedSymbols) return
if (!inProcess.add(current)) {
isThereLoop = true
return
}
when (val fir = current.fir) {
is FirClass -> {
fir.superConeTypes.forEach {
it.lookupTag.toSymbol(session)?.let(::dfs)
}
}
is FirTypeAlias -> {
fir.expandedConeType?.lookupTag?.toSymbol(session)?.let(::dfs)
}
else -> {}
}
visitedSymbols.add(current)
inProcess.remove(current)
}
dfs(symbol)
return isThereLoop
}
fun lookupSuperTypes(
symbol: FirClassifierSymbol<*>,
lookupInterfaces: Boolean,
deep: Boolean,
useSiteSession: FirSession,
supertypeSupplier: SupertypeSupplier = SupertypeSupplier.Default
): List<ConeClassLikeType> {
return SmartList<ConeClassLikeType>().also {
symbol.collectSuperTypes(it, SmartSet.create(), deep, lookupInterfaces, false, useSiteSession, supertypeSupplier)
}
}
inline fun <reified ID : Any, reified FS : FirScope> scopeSessionKey(): ScopeSessionKey<ID, FS> {
return object : ScopeSessionKey<ID, FS>() {}
}
val USE_SITE = scopeSessionKey<FirClassSymbol<*>, FirTypeScope>()
/* TODO REMOVE */
fun createSubstitution(
typeParameters: List<FirTypeParameterRef>, // TODO: or really declared?
type: ConeClassLikeType,
session: FirSession
): Map<FirTypeParameterSymbol, ConeKotlinType> {
val capturedOrType = session.typeContext.captureFromArguments(type, CaptureStatus.FROM_EXPRESSION) ?: type
val typeArguments = (capturedOrType as ConeClassLikeType).typeArguments
return typeParameters.zip(typeArguments) { typeParameter, typeArgument ->
val typeParameterSymbol = typeParameter.symbol
typeParameterSymbol to when (typeArgument) {
is ConeKotlinTypeProjection -> {
typeArgument.type
}
else /* StarProjection */ -> {
ConeTypeIntersector.intersectTypes(
session.typeContext,
typeParameterSymbol.fir.bounds.map { it.coneType }
)
}
}
}.toMap()
}
private fun ConeClassLikeType.computePartialExpansion(
useSiteSession: FirSession,
supertypeSupplier: SupertypeSupplier
): ConeClassLikeType = fullyExpandedType(useSiteSession) { supertypeSupplier.expansionForTypeAlias(it, useSiteSession) }
private fun FirClassifierSymbol<*>.collectSuperTypes(
list: MutableList<ConeClassLikeType>,
visitedSymbols: MutableSet<FirClassifierSymbol<*>>,
deep: Boolean,
lookupInterfaces: Boolean,
substituteSuperTypes: Boolean,
useSiteSession: FirSession,
supertypeSupplier: SupertypeSupplier
) {
if (!visitedSymbols.add(this)) return
when (this) {
is FirClassSymbol<*> -> {
val superClassTypes =
supertypeSupplier.forClass(fir, useSiteSession).mapNotNull {
it.computePartialExpansion(useSiteSession, supertypeSupplier)
.takeIf { type -> lookupInterfaces || type.isClassBasedType(useSiteSession) }
}
list += superClassTypes
if (deep)
superClassTypes.forEach {
if (it !is ConeClassErrorType) {
if (substituteSuperTypes) {
val substitutedTypes = SmartList<ConeClassLikeType>()
it.lookupTag.toSymbol(useSiteSession)?.collectSuperTypes(
substitutedTypes,
visitedSymbols,
deep,
lookupInterfaces,
substituteSuperTypes,
useSiteSession,
supertypeSupplier
)
val substitutor = createSubstitutionForSupertype(it, useSiteSession)
substitutedTypes.mapTo(list) { superType -> substitutor.substituteOrSelf(superType) as ConeClassLikeType }
} else {
it.lookupTag.toSymbol(useSiteSession)?.collectSuperTypes(
list,
visitedSymbols,
deep,
lookupInterfaces,
substituteSuperTypes,
useSiteSession,
supertypeSupplier
)
}
}
}
}
is FirTypeAliasSymbol -> {
val expansion = supertypeSupplier
.expansionForTypeAlias(fir, useSiteSession)
?.computePartialExpansion(useSiteSession, supertypeSupplier)
?: return
expansion.lookupTag.toSymbol(useSiteSession)
?.collectSuperTypes(list, visitedSymbols, deep, lookupInterfaces, substituteSuperTypes, useSiteSession, supertypeSupplier)
}
else -> error("?!id:1")
}
}
private fun ConeClassLikeType?.isClassBasedType(
useSiteSession: FirSession
): Boolean {
if (this is ConeClassErrorType) return false
val symbol = this?.lookupTag?.toSymbol(useSiteSession) as? FirClassSymbol ?: return false
return when (symbol) {
is FirAnonymousObjectSymbol -> true
is FirRegularClassSymbol -> symbol.fir.classKind == ClassKind.CLASS
}
}
fun createSubstitutionForSupertype(superType: ConeLookupTagBasedType, session: FirSession): ConeSubstitutor {
val klass = superType.lookupTag.toSymbol(session)?.fir as? FirRegularClass ?: return ConeSubstitutor.Empty
val arguments = superType.typeArguments.map {
it as? ConeKotlinType ?: ConeClassErrorType(ConeSimpleDiagnostic("illegal projection usage", DiagnosticKind.IllegalProjectionUsage))
}
val mapping = klass.typeParameters.map { it.symbol }.zip(arguments).toMap()
return ConeSubstitutorByMap(mapping, session)
}
@@ -0,0 +1,158 @@
/*
* Copyright 2010-2021 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.resolve
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType
import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.fir.utils.WeakPair
import org.jetbrains.kotlin.fir.utils.component1
import org.jetbrains.kotlin.fir.utils.component2
fun ConeClassLikeType.fullyExpandedType(
useSiteSession: FirSession,
expandedConeType: (FirTypeAlias) -> ConeClassLikeType? = { alias ->
alias.ensureResolved(FirResolvePhase.SUPER_TYPES)
alias.expandedConeType
},
): ConeClassLikeType {
if (this is ConeClassLikeTypeImpl) {
val (cachedSession, cachedExpandedType) = cachedExpandedType
if (cachedSession === useSiteSession && cachedExpandedType != null) {
return cachedExpandedType
}
val computedExpandedType = fullyExpandedTypeNoCache(useSiteSession, expandedConeType)
this.cachedExpandedType = WeakPair(useSiteSession, computedExpandedType)
return computedExpandedType
}
return fullyExpandedTypeNoCache(useSiteSession, expandedConeType)
}
fun ConeKotlinType.fullyExpandedType(
useSiteSession: FirSession
): ConeKotlinType = when (this) {
is ConeFlexibleType ->
ConeFlexibleType(lowerBound.fullyExpandedType(useSiteSession), upperBound.fullyExpandedType(useSiteSession))
is ConeClassLikeType -> fullyExpandedType(useSiteSession)
else -> this
}
private fun ConeClassLikeType.fullyExpandedTypeNoCache(
useSiteSession: FirSession,
expandedConeType: (FirTypeAlias) -> ConeClassLikeType?,
): ConeClassLikeType {
val directExpansionType = directExpansionType(useSiteSession, expandedConeType) ?: return this
return directExpansionType.fullyExpandedType(useSiteSession, expandedConeType)
}
fun ConeClassLikeType.directExpansionType(
useSiteSession: FirSession,
expandedConeType: (FirTypeAlias) -> ConeClassLikeType? = { alias ->
alias.ensureResolved(FirResolvePhase.SUPER_TYPES)
alias.expandedConeType
},
): ConeClassLikeType? {
val typeAliasSymbol = lookupTag.toSymbol(useSiteSession) as? FirTypeAliasSymbol ?: return null
val typeAlias = typeAliasSymbol.fir
val resultType = expandedConeType(typeAlias)
?.applyNullabilityFrom(useSiteSession, this)
?.applyAttributesFrom(useSiteSession, this)
?: return null
if (resultType.typeArguments.isEmpty()) return resultType
return mapTypeAliasArguments(typeAlias, this, resultType, useSiteSession) as? ConeClassLikeType
}
private fun ConeClassLikeType.applyNullabilityFrom(
session: FirSession,
abbreviation: ConeClassLikeType
): ConeClassLikeType {
if (abbreviation.isMarkedNullable) return withNullability(ConeNullability.NULLABLE, session.typeContext)
return this
}
private fun ConeClassLikeType.applyAttributesFrom(
session: FirSession,
abbreviation: ConeClassLikeType
): ConeClassLikeType {
val combinedAttributes = attributes.add(abbreviation.attributes)
return withAttributes(combinedAttributes, session.typeContext)
}
private fun mapTypeAliasArguments(
typeAlias: FirTypeAlias,
abbreviatedType: ConeClassLikeType,
resultingType: ConeClassLikeType,
useSiteSession: FirSession,
): ConeKotlinType {
if (typeAlias.typeParameters.isNotEmpty() && abbreviatedType.typeArguments.isEmpty()) {
return resultingType.lookupTag.constructClassType(emptyArray(), resultingType.isNullable)
}
val typeAliasMap = typeAlias.typeParameters.map { it.symbol }.zip(abbreviatedType.typeArguments).toMap()
val substitutor = object : AbstractConeSubstitutor(useSiteSession.typeContext) {
override fun substituteType(type: ConeKotlinType): ConeKotlinType? {
return null
}
override fun substituteArgument(projection: ConeTypeProjection): ConeTypeProjection? {
val type = (projection as? ConeKotlinTypeProjection)?.type ?: return null
val symbol = (type as? ConeTypeParameterType)?.lookupTag?.symbol ?: return super.substituteArgument(projection)
val mappedProjection = typeAliasMap[symbol] ?: return super.substituteArgument(projection)
var mappedType = (mappedProjection as? ConeKotlinTypeProjection)?.type.updateNullabilityIfNeeded(type)
mappedType = when (mappedType) {
is ConeClassErrorType,
is ConeClassLikeTypeImpl,
is ConeDefinitelyNotNullType,
is ConeTypeParameterTypeImpl,
is ConeFlexibleType -> {
mappedType.withAttributes(type.attributes.add(mappedType.attributes), useSiteSession.typeContext)
}
null -> return mappedProjection
else -> mappedType
}
fun convertProjectionKindToConeTypeProjection(projectionKind: ProjectionKind): ConeTypeProjection {
return when (projectionKind) {
ProjectionKind.STAR -> ConeStarProjection
ProjectionKind.IN -> ConeKotlinTypeProjectionIn(mappedType)
ProjectionKind.OUT -> ConeKotlinTypeProjectionOut(mappedType)
ProjectionKind.INVARIANT -> mappedType
}
}
if (mappedProjection.kind == projection.kind) {
return convertProjectionKindToConeTypeProjection(mappedProjection.kind)
}
if (mappedProjection.kind == ProjectionKind.STAR || projection.kind == ProjectionKind.STAR) {
return ConeStarProjection
}
if (mappedProjection.kind == ProjectionKind.INVARIANT) {
return convertProjectionKindToConeTypeProjection(projection.kind)
}
if (projection.kind == ProjectionKind.INVARIANT) {
return convertProjectionKindToConeTypeProjection(mappedProjection.kind)
}
return ConeKotlinTypeConflictingProjection(mappedType)
}
}
return substitutor.substituteOrSelf(resultingType)
}
@@ -0,0 +1,175 @@
/*
* Copyright 2010-2021 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.resolve.calls
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildExpressionWithSmartcast
import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpression
import org.jetbrains.kotlin.fir.references.builder.buildImplicitThisReference
import org.jetbrains.kotlin.fir.renderWithType
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.constructType
import org.jetbrains.kotlin.fir.resolve.scope
import org.jetbrains.kotlin.fir.resolve.smartcastScope
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.types.SmartcastStability
interface Receiver
interface ReceiverValue : Receiver {
val type: ConeKotlinType
val receiverExpression: FirExpression
fun scope(useSiteSession: FirSession, scopeSession: ScopeSession): FirTypeScope? =
type.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing)
}
// TODO: should inherit just Receiver, not ReceiverValue
abstract class AbstractExplicitReceiver<E : FirExpression> : Receiver {
abstract val explicitReceiver: FirExpression
}
abstract class AbstractExplicitReceiverValue<E : FirExpression> : AbstractExplicitReceiver<E>(), ReceiverValue {
override val type: ConeKotlinType
// NB: safe cast is necessary here
get() = explicitReceiver.typeRef.coneTypeSafe()
?: ConeKotlinErrorType(ConeIntermediateDiagnostic("No type calculated for: ${explicitReceiver.renderWithType()}")) // TODO: assert here
override val receiverExpression: FirExpression
get() = explicitReceiver
}
open class ExpressionReceiverValue(
override val explicitReceiver: FirExpression
) : AbstractExplicitReceiverValue<FirExpression>(), ReceiverValue {
override fun scope(useSiteSession: FirSession, scopeSession: ScopeSession): FirTypeScope? {
var receiverExpr: FirExpression? = receiverExpression
// Unwrap `x!!` to `x` and use the resulted expression to derive receiver type. This is necessary so that smartcast types inside
// `!!` is handled correctly.
if (receiverExpr is FirCheckNotNullCall) {
receiverExpr = receiverExpr.arguments.firstOrNull()
}
if (receiverExpr is FirExpressionWithSmartcast) {
return receiverExpr.smartcastScope(useSiteSession, scopeSession)
}
return type.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing)
}
}
sealed class ImplicitReceiverValue<S : FirBasedSymbol<*>>(
val boundSymbol: S,
type: ConeKotlinType,
protected val useSiteSession: FirSession,
protected val scopeSession: ScopeSession,
private val mutable: Boolean,
) : ReceiverValue {
final override var type: ConeKotlinType = type
private set
val originalType: ConeKotlinType = type
var implicitScope: FirTypeScope? = type.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing)
private set
override fun scope(useSiteSession: FirSession, scopeSession: ScopeSession): FirTypeScope? = implicitScope
private val originalReceiverExpression: FirThisReceiverExpression = receiverExpression(boundSymbol, type)
final override var receiverExpression: FirExpression = originalReceiverExpression
private set
/*
* Should be called only in ImplicitReceiverStack
*/
fun replaceType(type: ConeKotlinType) {
if (!mutable) throw IllegalStateException("Cannot mutate an immutable ImplicitReceiverValue")
if (type == this.type) return
this.type = type
receiverExpression = if (type == originalReceiverExpression.typeRef.coneType) {
originalReceiverExpression
} else {
buildExpressionWithSmartcast {
originalExpression = originalReceiverExpression
smartcastType = originalReceiverExpression.typeRef.resolvedTypeFromPrototype(type)
typesFromSmartCast = listOf(type)
smartcastStability = SmartcastStability.STABLE_VALUE
}
}
implicitScope = type.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing)
}
abstract fun createSnapshot(): ImplicitReceiverValue<S>
}
private fun receiverExpression(symbol: FirBasedSymbol<*>, type: ConeKotlinType): FirThisReceiverExpression =
buildThisReceiverExpression {
// NB: we can't use `symbol.fir.source` as the source of `this` receiver. For instance, if this is an implicit receiver for a class,
// the entire class itself will be set as a source. If combined with an implicit type operation, a certain assertion, like null
// check assertion, will retrieve source as an assertion message, which is literally the entire class (!).
calleeReference = buildImplicitThisReference {
boundSymbol = symbol
}
typeRef = buildResolvedTypeRef {
this.type = type
}
isImplicit = true
}
class ImplicitDispatchReceiverValue(
boundSymbol: FirClassSymbol<*>,
type: ConeKotlinType,
useSiteSession: FirSession,
scopeSession: ScopeSession,
mutable: Boolean = true,
) : ImplicitReceiverValue<FirClassSymbol<*>>(boundSymbol, type, useSiteSession, scopeSession, mutable) {
constructor(
boundSymbol: FirClassSymbol<*>, useSiteSession: FirSession, scopeSession: ScopeSession
) : this(
boundSymbol, boundSymbol.constructType(typeArguments = emptyArray(), isNullable = false),
useSiteSession, scopeSession
)
override fun createSnapshot(): ImplicitReceiverValue<FirClassSymbol<*>> {
return ImplicitDispatchReceiverValue(boundSymbol, type, useSiteSession, scopeSession, false)
}
}
class ImplicitExtensionReceiverValue(
boundSymbol: FirCallableSymbol<*>,
type: ConeKotlinType,
useSiteSession: FirSession,
scopeSession: ScopeSession,
mutable: Boolean = true,
) : ImplicitReceiverValue<FirCallableSymbol<*>>(boundSymbol, type, useSiteSession, scopeSession, mutable) {
override fun createSnapshot(): ImplicitReceiverValue<FirCallableSymbol<*>> {
return ImplicitExtensionReceiverValue(boundSymbol, type, useSiteSession, scopeSession, false)
}
}
class InaccessibleImplicitReceiverValue(
boundSymbol: FirClassSymbol<*>,
type: ConeKotlinType,
useSiteSession: FirSession,
scopeSession: ScopeSession,
mutable: Boolean = true,
) : ImplicitReceiverValue<FirClassSymbol<*>>(boundSymbol, type, useSiteSession, scopeSession, mutable) {
override fun createSnapshot(): ImplicitReceiverValue<FirClassSymbol<*>> {
return InaccessibleImplicitReceiverValue(boundSymbol, type, useSiteSession, scopeSession, false)
}
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2021 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.resolve.calls
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.name.Name
abstract class FirSyntheticNamesProvider : FirSessionComponent {
abstract fun possibleGetterNamesByPropertyName(name: Name): List<Name>
abstract fun setterNameByGetterName(name: Name): Name?
abstract fun getterNameBySetterName(name: Name): Name?
abstract fun possiblePropertyNamesByAccessorName(name: Name): List<Name>
}
val FirSession.syntheticNamesProvider: FirSyntheticNamesProvider by FirSession.sessionComponentAccessor()
@@ -0,0 +1,146 @@
/*
* Copyright 2010-2021 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.resolve.calls
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.getDeprecationsFromAccessors
import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.symbols.SyntheticSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirAccessorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeNullability.NOT_NULL
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.typeContext
import org.jetbrains.kotlin.fir.types.withNullability
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.types.AbstractTypeChecker
class FirSyntheticPropertySymbol(
callableId: CallableId,
override val accessorId: CallableId
) : FirAccessorSymbol(callableId, accessorId), SyntheticSymbol
class FirSyntheticFunctionSymbol(
callableId: CallableId
) : FirNamedFunctionSymbol(callableId), SyntheticSymbol
class FirSyntheticPropertiesScope(
val session: FirSession,
private val baseScope: FirTypeScope
) : FirContainingNamesAwareScope() {
private val syntheticNamesProvider = session.syntheticNamesProvider
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
val getterNames = syntheticNamesProvider.possibleGetterNamesByPropertyName(name)
for (getterName in getterNames) {
baseScope.processFunctionsByName(getterName) {
checkGetAndCreateSynthetic(name, getterName, it, processor)
}
}
}
override fun getCallableNames(): Set<Name> = baseScope.getCallableNames().flatMapTo(hashSetOf()) { propertyName ->
syntheticNamesProvider.possiblePropertyNamesByAccessorName(propertyName)
}
override fun getClassifierNames(): Set<Name> = emptySet()
private fun checkGetAndCreateSynthetic(
propertyName: Name,
getterName: Name,
getterSymbol: FirFunctionSymbol<*>,
processor: (FirVariableSymbol<*>) -> Unit
) {
if (getterSymbol !is FirNamedFunctionSymbol) return
val getter = getterSymbol.fir
if (getter.typeParameters.isNotEmpty()) return
if (getter.valueParameters.isNotEmpty()) return
if (getter.isStatic) return
val getterReturnType = (getter.returnTypeRef as? FirResolvedTypeRef)?.type
if ((getterReturnType as? ConeClassLikeType)?.lookupTag?.classId == StandardClassIds.Unit) return
if (!getterSymbol.hasJavaOverridden()) return
var matchingSetter: FirSimpleFunction? = null
if (getterReturnType != null) {
val setterName = syntheticNamesProvider.setterNameByGetterName(getterName)
if (setterName != null) {
baseScope.processFunctionsByName(setterName, fun(setterSymbol: FirFunctionSymbol<*>) {
if (matchingSetter != null) return
val setter = setterSymbol.fir as? FirSimpleFunction ?: return
val parameter = setter.valueParameters.singleOrNull() ?: return
if (setter.typeParameters.isNotEmpty() || setter.isStatic) return
val parameterType = (parameter.returnTypeRef as? FirResolvedTypeRef)?.type ?: return
// TODO: at this moment it works for cases like
// class Base {
// void setSomething(Object value) {}
// }
// class Derived extends Base {
// String getSomething() { return ""; }
// }
// In FE 1.0, we should have also Object getSomething() in class Base for this to work
// I think details here are worth designing
if (!AbstractTypeChecker.isSubtypeOf(
session.typeContext,
getterReturnType.withNullability(NOT_NULL, session.typeContext),
parameterType.withNullability(NOT_NULL, session.typeContext)
)
) {
return
}
matchingSetter = setter
})
}
}
val classLookupTag = getterSymbol.originalOrSelf().dispatchReceiverClassOrNull()
val packageName = classLookupTag?.classId?.packageFqName ?: getterSymbol.callableId.packageName
val className = classLookupTag?.classId?.relativeClassName
val property = buildSyntheticProperty {
moduleData = session.moduleData
name = propertyName
symbol = FirSyntheticPropertySymbol(
accessorId = getterSymbol.callableId,
callableId = CallableId(packageName, className, propertyName)
)
delegateGetter = getter
delegateSetter = matchingSetter
deprecation = getDeprecationsFromAccessors(getter, matchingSetter, session.languageVersionSettings.apiVersion)
}
val syntheticSymbol = property.symbol
(baseScope as? FirUnstableSmartcastTypeScope)?.apply {
if (isSymbolFromUnstableSmartcast(getterSymbol)) {
markSymbolFromUnstableSmartcast(syntheticSymbol)
}
}
processor(syntheticSymbol)
}
private fun FirNamedFunctionSymbol.hasJavaOverridden(): Boolean {
var result = false
baseScope.processOverriddenFunctionsAndSelf(this) {
if (it.unwrapFakeOverrides().fir.origin == FirDeclarationOrigin.Enhancement) {
result = true
ProcessorAction.STOP
} else {
ProcessorAction.NEXT
}
}
return result
}
}
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2021 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.resolve.providers
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@RequiresOptIn
annotation class FirProviderInternals
abstract class FirProvider : FirSessionComponent {
/**
* [symbolProvider] for [FirProvider] may provide only symbols from sources of current module
*/
abstract val symbolProvider: FirSymbolProvider
open val isPhasedFirAllowed: Boolean get() = false
abstract fun getFirClassifierByFqName(classId: ClassId): FirClassLikeDeclaration?
abstract fun getFirClassifierContainerFile(fqName: ClassId): FirFile
abstract fun getFirClassifierContainerFileIfAny(fqName: ClassId): FirFile?
open fun getFirClassifierContainerFile(symbol: FirClassLikeSymbol<*>): FirFile =
getFirClassifierContainerFile(symbol.classId)
open fun getFirClassifierContainerFileIfAny(symbol: FirClassLikeSymbol<*>): FirFile? =
getFirClassifierContainerFileIfAny(symbol.classId)
abstract fun getFirCallableContainerFile(symbol: FirCallableSymbol<*>): FirFile?
abstract fun getFirFilesByPackage(fqName: FqName): List<FirFile>
@FirProviderInternals
abstract fun recordGeneratedClass(owner: FirAnnotatedDeclaration, klass: FirRegularClass)
@FirProviderInternals
abstract fun recordGeneratedMember(owner: FirAnnotatedDeclaration, klass: FirDeclaration)
abstract fun getClassNamesInPackage(fqName: FqName): Set<Name>
}
val FirSession.firProvider: FirProvider by FirSession.sessionComponentAccessor()
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2021 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.resolve.providers
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.resolve.getSymbolByLookupTag
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.getDeclaredConstructors
import org.jetbrains.kotlin.fir.scopes.getFunctions
import org.jetbrains.kotlin.fir.scopes.getProperties
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeLookupTagBasedType
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@RequiresOptIn
annotation class FirSymbolProviderInternals
abstract class FirSymbolProvider(val session: FirSession) : FirSessionComponent {
abstract fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>?
@OptIn(ExperimentalStdlibApi::class, FirSymbolProviderInternals::class)
open fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List<FirCallableSymbol<*>> {
return buildList { getTopLevelCallableSymbolsTo(this, packageFqName, name) }
}
@FirSymbolProviderInternals
abstract fun getTopLevelCallableSymbolsTo(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name)
@OptIn(ExperimentalStdlibApi::class, FirSymbolProviderInternals::class)
open fun getTopLevelFunctionSymbols(packageFqName: FqName, name: Name): List<FirNamedFunctionSymbol> {
return buildList { getTopLevelFunctionSymbolsTo(this, packageFqName, name) }
}
@FirSymbolProviderInternals
abstract fun getTopLevelFunctionSymbolsTo(destination: MutableList<FirNamedFunctionSymbol>, packageFqName: FqName, name: Name)
@OptIn(ExperimentalStdlibApi::class, FirSymbolProviderInternals::class)
open fun getTopLevelPropertySymbols(packageFqName: FqName, name: Name): List<FirPropertySymbol> {
return buildList { getTopLevelPropertySymbolsTo(this, packageFqName, name) }
}
@FirSymbolProviderInternals
abstract fun getTopLevelPropertySymbolsTo(destination: MutableList<FirPropertySymbol>, packageFqName: FqName, name: Name)
abstract fun getPackage(fqName: FqName): FqName? // TODO: Replace to symbol sometime
}
abstract class FirDependenciesSymbolProvider(session: FirSession) : FirSymbolProvider(session)
private fun FirSymbolProvider.getClassDeclaredMemberScope(classId: ClassId): FirScope? {
val classSymbol = getClassLikeSymbolByClassId(classId) as? FirRegularClassSymbol ?: return null
return session.declaredMemberScope(classSymbol.fir)
}
fun FirSymbolProvider.getClassDeclaredConstructors(classId: ClassId): List<FirConstructorSymbol> {
val classMemberScope = getClassDeclaredMemberScope(classId)
return classMemberScope?.getDeclaredConstructors().orEmpty()
}
fun FirSymbolProvider.getClassDeclaredFunctionSymbols(classId: ClassId, name: Name): List<FirNamedFunctionSymbol> {
val classMemberScope = getClassDeclaredMemberScope(classId)
return classMemberScope?.getFunctions(name).orEmpty()
}
fun FirSymbolProvider.getClassDeclaredPropertySymbols(classId: ClassId, name: Name): List<FirVariableSymbol<*>> {
val classMemberScope = getClassDeclaredMemberScope(classId)
return classMemberScope?.getProperties(name).orEmpty()
}
inline fun <reified T : FirBasedSymbol<*>> FirSymbolProvider.getSymbolByTypeRef(typeRef: FirTypeRef): T? {
val lookupTag = typeRef.coneTypeSafe<ConeLookupTagBasedType>()?.lookupTag ?: return null
return getSymbolByLookupTag(lookupTag) as? T
}
val FirSession.symbolProvider: FirSymbolProvider by FirSession.sessionComponentAccessor()
val FirSession.dependenciesSymbolProvider: FirSymbolProvider by FirSession.sessionComponentAccessor<FirDependenciesSymbolProvider>()
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2018 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.resolve.providers.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.NoMutableState
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@NoMutableState
class FirCompositeSymbolProvider(session: FirSession, val providers: List<FirSymbolProvider>) : FirSymbolProvider(session) {
override fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List<FirCallableSymbol<*>> {
return providers.flatMap { it.getTopLevelCallableSymbols(packageFqName, name) }
}
@FirSymbolProviderInternals
override fun getTopLevelCallableSymbolsTo(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) {
destination += getTopLevelCallableSymbols(packageFqName, name)
}
@FirSymbolProviderInternals
override fun getTopLevelFunctionSymbolsTo(destination: MutableList<FirNamedFunctionSymbol>, packageFqName: FqName, name: Name) {
providers.forEach {
it.getTopLevelFunctionSymbolsTo(destination, packageFqName, name)
}
}
@FirSymbolProviderInternals
override fun getTopLevelPropertySymbolsTo(destination: MutableList<FirPropertySymbol>, packageFqName: FqName, name: Name) {
providers.forEach {
it.getTopLevelPropertySymbolsTo(destination, packageFqName, name)
}
}
override fun getPackage(fqName: FqName): FqName? {
return providers.firstNotNullOfOrNull { it.getPackage(fqName) }
}
override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? {
return providers.firstNotNullOfOrNull { it.getClassLikeSymbolByClassId(classId) }
}
}
@@ -0,0 +1,90 @@
/*
* Copyright 2010-2021 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.resolve.providers.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.ThreadSafeMutableState
import org.jetbrains.kotlin.fir.caches.createCache
import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.caches.getValue
import org.jetbrains.kotlin.fir.nullableModuleData
import org.jetbrains.kotlin.fir.resolve.providers.FirDependenciesSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@ThreadSafeMutableState
open class FirDependenciesSymbolProviderImpl(session: FirSession) : FirDependenciesSymbolProvider(session) {
private val classCache = session.firCachesFactory.createCache(::computeClass)
private val topLevelCallableCache = session.firCachesFactory.createCache(::computeTopLevelCallables)
private val topLevelFunctionCache = session.firCachesFactory.createCache(::computeTopLevelFunctions)
private val topLevelPropertyCache = session.firCachesFactory.createCache(::computeTopLevelProperties)
private val packageCache = session.firCachesFactory.createCache(::computePackage)
protected open val dependencyProviders by lazy {
val moduleData = session.nullableModuleData ?: return@lazy emptyList()
(moduleData.dependencies + moduleData.friendDependencies + moduleData.dependsOnDependencies).mapNotNull {
session.sessionProvider?.getSession(it)?.symbolProvider
}
}
@OptIn(FirSymbolProviderInternals::class, ExperimentalStdlibApi::class)
private fun computeTopLevelCallables(callableId: CallableId): List<FirCallableSymbol<*>> = buildList {
dependencyProviders.forEach { it.getTopLevelCallableSymbolsTo(this, callableId.packageName, callableId.callableName) }
}
@OptIn(FirSymbolProviderInternals::class, ExperimentalStdlibApi::class)
private fun computeTopLevelFunctions(callableId: CallableId): List<FirNamedFunctionSymbol> = buildList {
dependencyProviders.forEach { it.getTopLevelFunctionSymbolsTo(this, callableId.packageName, callableId.callableName) }
}
@OptIn(FirSymbolProviderInternals::class, ExperimentalStdlibApi::class)
private fun computeTopLevelProperties(callableId: CallableId): List<FirPropertySymbol> = buildList {
dependencyProviders.forEach { it.getTopLevelPropertySymbolsTo(this, callableId.packageName, callableId.callableName) }
}
private fun computePackage(it: FqName): FqName? =
dependencyProviders.firstNotNullOfOrNull { provider -> provider.getPackage(it) }
private fun computeClass(classId: ClassId): FirClassLikeSymbol<*>? =
dependencyProviders.firstNotNullOfOrNull { provider -> provider.getClassLikeSymbolByClassId(classId) }
@FirSymbolProviderInternals
override fun getTopLevelFunctionSymbolsTo(destination: MutableList<FirNamedFunctionSymbol>, packageFqName: FqName, name: Name) {
destination += topLevelFunctionCache.getValue(CallableId(packageFqName, name))
}
@FirSymbolProviderInternals
override fun getTopLevelPropertySymbolsTo(destination: MutableList<FirPropertySymbol>, packageFqName: FqName, name: Name) {
destination += topLevelPropertyCache.getValue(CallableId(packageFqName, name))
}
@FirSymbolProviderInternals
override fun getTopLevelCallableSymbolsTo(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) {
destination += getTopLevelCallableSymbols(packageFqName, name)
}
override fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List<FirCallableSymbol<*>> {
return topLevelCallableCache.getValue(CallableId(packageFqName, name))
}
override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? {
return classCache.getValue(classId)
}
override fun getPackage(fqName: FqName): FqName? {
return packageCache.getValue(fqName)
}
}
@@ -0,0 +1,188 @@
/*
* Copyright 2010-2021 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.resolve.substitution
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.resolve.withCombinedCustomAttributesFrom
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
import org.jetbrains.kotlin.types.model.typeConstructor
abstract class AbstractConeSubstitutor(private val typeContext: ConeTypeContext) : ConeSubstitutor() {
private fun wrapProjection(old: ConeTypeProjection, newType: ConeKotlinType): ConeTypeProjection {
return when (old) {
is ConeStarProjection -> old
is ConeKotlinTypeProjectionIn -> ConeKotlinTypeProjectionIn(newType)
is ConeKotlinTypeProjectionOut -> ConeKotlinTypeProjectionOut(newType)
is ConeKotlinTypeConflictingProjection -> ConeKotlinTypeConflictingProjection(newType)
is ConeKotlinType -> newType
else -> old
}
}
abstract fun substituteType(type: ConeKotlinType): ConeKotlinType?
open fun substituteArgument(projection: ConeTypeProjection): ConeTypeProjection? {
val type = (projection as? ConeKotlinTypeProjection)?.type ?: return null
val newType = substituteOrNull(type) ?: return null
return wrapProjection(projection, newType)
}
fun ConeKotlinType?.updateNullabilityIfNeeded(originalType: ConeKotlinType): ConeKotlinType? {
return when {
originalType is ConeDefinitelyNotNullType -> this?.withNullability(ConeNullability.NOT_NULL, typeContext)
originalType.isMarkedNullable -> this?.withNullability(ConeNullability.NULLABLE, typeContext)
else -> this
}
}
override fun substituteOrNull(type: ConeKotlinType): ConeKotlinType? {
val newType = substituteType(type)
if (newType != null && type is ConeDefinitelyNotNullType) {
return newType.makeConeTypeDefinitelyNotNullOrNotNull(typeContext)
}
return (newType ?: type.substituteRecursive())
}
private fun ConeKotlinType.substituteRecursive(): ConeKotlinType? {
return when (this) {
is ConeClassErrorType -> return null
is ConeClassLikeType -> this.substituteArguments()
is ConeLookupTagBasedType -> return null
is ConeFlexibleType -> this.substituteBounds()?.let {
// TODO: may be (?) it's worth adding regular type comparison via AbstractTypeChecker
// However, the simplified check here should be enough for typical flexible types
if (it.lowerBound == it.upperBound) it.lowerBound
else it
}
is ConeCapturedType -> return null
is ConeDefinitelyNotNullType -> this.substituteOriginal()
is ConeIntersectionType -> this.substituteIntersectedTypes()
is ConeStubType -> return null
is ConeIntegerLiteralType -> return null
}
}
private fun ConeIntersectionType.substituteIntersectedTypes(): ConeIntersectionType? {
val substitutedTypes = ArrayList<ConeKotlinType>(intersectedTypes.size)
var somethingIsSubstituted = false
for (type in intersectedTypes) {
val substitutedType = substituteOrNull(type)?.also {
somethingIsSubstituted = true
} ?: type
substitutedTypes += substitutedType
}
if (!somethingIsSubstituted) return null
return ConeIntersectionType(substitutedTypes)
}
private fun ConeDefinitelyNotNullType.substituteOriginal(): ConeKotlinType? {
val substituted = substituteOrNull(original)
?.withNullability(ConeNullability.NOT_NULL, typeContext)
?.withAttributes(original.attributes, typeContext)
?: return null
return ConeDefinitelyNotNullType.create(substituted, typeContext) ?: substituted
}
private fun ConeFlexibleType.substituteBounds(): ConeFlexibleType? {
val newLowerBound = substituteOrNull(lowerBound)
val newUpperBound = substituteOrNull(upperBound)
if (newLowerBound != null || newUpperBound != null) {
return ConeFlexibleType(
newLowerBound?.lowerBoundIfFlexible() ?: lowerBound,
newUpperBound?.upperBoundIfFlexible() ?: upperBound
)
}
return null
}
private fun ConeKotlinType.substituteArguments(): ConeKotlinType? {
val newArguments by lazy { arrayOfNulls<ConeTypeProjection>(typeArguments.size) }
var initialized = false
for ((index, typeArgument) in this.typeArguments.withIndex()) {
newArguments[index] = substituteArgument(typeArgument)?.also {
initialized = true
}
}
if (initialized) {
for ((index, typeArgument) in this.typeArguments.withIndex()) {
if (newArguments[index] == null) {
newArguments[index] = typeArgument
}
}
@Suppress("UNCHECKED_CAST")
return when (this) {
is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(
lookupTag,
newArguments as Array<ConeTypeProjection>,
nullability.isNullable,
attributes
)
is ConeClassLikeType -> error("Unknown class-like type to substitute: $this, ${this::class}")
else -> error("Unknown type to substitute: $this, ${this::class}")
}
}
return null
}
}
fun substitutorByMap(substitution: Map<FirTypeParameterSymbol, ConeKotlinType>, useSiteSession: FirSession): ConeSubstitutor {
// If all arguments match parameters, then substitutor isn't needed
if (substitution.all { (parameterSymbol, argumentType) ->
(argumentType as? ConeTypeParameterType)?.lookupTag?.typeParameterSymbol == parameterSymbol
}
) return ConeSubstitutor.Empty
return ConeSubstitutorByMap(substitution, useSiteSession)
}
data class ChainedSubstitutor(private val first: ConeSubstitutor, private val second: ConeSubstitutor) : ConeSubstitutor() {
override fun substituteOrNull(type: ConeKotlinType): ConeKotlinType? {
first.substituteOrNull(type)?.let { return second.substituteOrSelf(it) }
return second.substituteOrNull(type)
}
}
fun ConeSubstitutor.chain(other: ConeSubstitutor): ConeSubstitutor {
if (this == ConeSubstitutor.Empty) return other
if (other == ConeSubstitutor.Empty) return this
return ChainedSubstitutor(this, other)
}
data class ConeSubstitutorByMap(
val substitution: Map<FirTypeParameterSymbol, ConeKotlinType>,
val useSiteSession: FirSession
) : AbstractConeSubstitutor(useSiteSession.typeContext) {
override fun substituteType(type: ConeKotlinType): ConeKotlinType? {
if (type !is ConeTypeParameterType) return null
val result =
substitution[type.lookupTag.symbol].updateNullabilityIfNeeded(type)
?.withCombinedCustomAttributesFrom(type, useSiteSession.typeContext)
?: return null
if (type.isUnsafeVarianceType(useSiteSession)) {
return useSiteSession.typeApproximator.approximateToSuperType(
result, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference
) ?: result
}
return result
}
}
fun createTypeSubstitutorByTypeConstructor(map: Map<TypeConstructorMarker, ConeKotlinType>, context: ConeTypeContext): ConeSubstitutor {
if (map.isEmpty()) return ConeSubstitutor.Empty
return object : AbstractConeSubstitutor(context), TypeSubstitutorMarker {
override fun substituteType(type: ConeKotlinType): ConeKotlinType? {
if (type !is ConeLookupTagBasedType && type !is ConeStubType) return null
val new = map[type.typeConstructor(context)] ?: return null
return new.approximateIntegerLiteralType().updateNullabilityIfNeeded(type)?.withCombinedCustomAttributesFrom(type, context)
}
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2021 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.resolve.transformers
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.coneTypeSafe
fun FirBasedSymbol<*>.ensureResolvedForCalls() {
if (fir.resolvePhase >= FirResolvePhase.DECLARATIONS) return
// val requiredPhase = when (fir) {
// is FirFunction, is FirProperty -> FirResolvePhase.CONTRACTS
// else -> FirResolvePhase.STATUS
// }
//
// if (requiredPhase == FirResolvePhase.CONTRACTS) {
// // Workaround for recursive contracts in CLI
// // Otherwise the assertion about presence of fir.session.phaseManager would fail
// // See org.jetbrains.kotlin.fir.FirOldFrontendDiagnosticsTestWithStdlibGenerated.Contracts.Dsl.Errors.testRecursiveContract
// if (fir.session.phaseManager == null) return
// }
val requiredPhase = FirResolvePhase.DECLARATIONS
ensureResolved(requiredPhase)
}
fun ConeKotlinType.ensureResolvedTypeDeclaration(
useSiteSession: FirSession,
requiredPhase: FirResolvePhase = FirResolvePhase.DECLARATIONS,
) {
if (this !is ConeClassLikeType) return
lookupTag.toSymbol(useSiteSession)?.ensureResolved(requiredPhase)
fullyExpandedType(useSiteSession).lookupTag.toSymbol(useSiteSession)?.ensureResolved(requiredPhase)
}
fun FirTypeRef.ensureResolvedTypeDeclaration(
useSiteSession: FirSession,
requiredPhase: FirResolvePhase = FirResolvePhase.DECLARATIONS,
) {
coneTypeSafe<ConeKotlinType>()?.ensureResolvedTypeDeclaration(useSiteSession, requiredPhase)
}
@@ -0,0 +1,59 @@
/*
* Copyright 2010-2021 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.scopes
import org.jetbrains.kotlin.fir.declarations.FirDeclarationAttributes
import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataKey
import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataRegistry
import org.jetbrains.kotlin.fir.declarations.FirTypedDeclaration
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
abstract class FakeOverrideTypeCalculator {
abstract fun computeReturnType(declaration: FirTypedDeclaration): FirTypeRef
object DoNothing : FakeOverrideTypeCalculator() {
override fun computeReturnType(declaration: FirTypedDeclaration): FirTypeRef {
return declaration.returnTypeRef
}
}
object Forced : FakeOverrideTypeCalculator() {
override fun computeReturnType(declaration: FirTypedDeclaration): FirResolvedTypeRef {
val fakeOverrideSubstitution = declaration.attributes.fakeOverrideSubstitution
?: return declaration.returnTypeRef as FirResolvedTypeRef
synchronized(fakeOverrideSubstitution) {
if (declaration.attributes.fakeOverrideSubstitution == null) {
return declaration.returnTypeRef as FirResolvedTypeRef
}
declaration.attributes.fakeOverrideSubstitution = null
val (substitutor, baseSymbol) = fakeOverrideSubstitution
val baseDeclaration = baseSymbol.fir as FirTypedDeclaration
val baseReturnType = computeReturnType(baseDeclaration).type
val coneType = substitutor.substituteOrSelf(baseReturnType)
val returnType = declaration.returnTypeRef.resolvedTypeFromPrototype(coneType)
declaration.replaceReturnTypeRef(returnType)
return returnType
}
}
}
}
// ---------------------------------------------------------------------------------------------------------------------------------------
object FakeOverrideSubstitutionKey : FirDeclarationDataKey()
var FirDeclarationAttributes.fakeOverrideSubstitution: FakeOverrideSubstitution? by FirDeclarationDataRegistry.attributesAccessor(
FakeOverrideSubstitutionKey
)
data class FakeOverrideSubstitution(
val substitutor: ConeSubstitutor,
val baseSymbol: FirBasedSymbol<*>
)
@@ -0,0 +1,174 @@
/*
* Copyright 2010-2021 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.scopes
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirField
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.isSynthetic
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.scopes.impl.*
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.ConeClassErrorType
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
class FirKotlinScopeProvider(
val declaredMemberScopeDecorator: (
klass: FirClass,
declaredMemberScope: FirContainingNamesAwareScope,
useSiteSession: FirSession,
scopeSession: ScopeSession
) -> FirContainingNamesAwareScope = { _, declaredMemberScope, _, _ -> declaredMemberScope }
) : FirScopeProvider(), FirSessionComponent {
override fun getUseSiteMemberScope(
klass: FirClass,
useSiteSession: FirSession,
scopeSession: ScopeSession
): FirTypeScope {
return scopeSession.getOrBuild(klass.symbol, USE_SITE) {
val declaredScope = useSiteSession.declaredMemberScope(klass)
val delegateFields = klass.declarations.filterIsInstance<FirField>().filter { it.isSynthetic }
val decoratedDeclaredMemberScope =
declaredMemberScopeDecorator(klass, declaredScope, useSiteSession, scopeSession).let {
if (delegateFields.isEmpty())
it
else
FirDelegatedMemberScope(useSiteSession, scopeSession, klass, it, delegateFields)
}
val scopes = lookupSuperTypes(klass, lookupInterfaces = true, deep = false, useSiteSession = useSiteSession)
.mapNotNull { useSiteSuperType ->
useSiteSuperType.scopeForSupertype(useSiteSession, scopeSession, klass)
}
FirClassUseSiteMemberScope(
useSiteSession,
FirTypeIntersectionScope.prepareIntersectionScope(
useSiteSession, FirStandardOverrideChecker(useSiteSession), scopes,
klass.defaultType(),
),
decoratedDeclaredMemberScope,
)
}
}
override fun getStaticMemberScopeForCallables(
klass: FirClass,
useSiteSession: FirSession,
scopeSession: ScopeSession
): FirContainingNamesAwareScope? {
return when (klass.classKind) {
ClassKind.ENUM_CLASS -> FirNameAwareOnlyCallablesScope(FirStaticScope(useSiteSession.declaredMemberScope(klass)))
else -> null
}
}
override fun getNestedClassifierScope(
klass: FirClass,
useSiteSession: FirSession,
scopeSession: ScopeSession
): FirContainingNamesAwareScope? {
return useSiteSession.nestedClassifierScope(klass)
}
}
data class ConeSubstitutionScopeKey(
val lookupTag: ConeClassLikeLookupTag, val isFromExpectClass: Boolean, val substitutor: ConeSubstitutor
) : ScopeSessionKey<FirClass, FirClassSubstitutionScope>()
fun FirClass.unsubstitutedScope(
useSiteSession: FirSession,
scopeSession: ScopeSession,
withForcedTypeCalculator: Boolean
): FirTypeScope {
val scope = scopeProvider.getUseSiteMemberScope(this, useSiteSession, scopeSession)
if (withForcedTypeCalculator) return FirScopeWithFakeOverrideTypeCalculator(scope, FakeOverrideTypeCalculator.Forced)
return scope
}
fun FirClassSymbol<*>.unsubstitutedScope(
useSiteSession: FirSession,
scopeSession: ScopeSession,
withForcedTypeCalculator: Boolean
): FirTypeScope {
return fir.unsubstitutedScope(useSiteSession, scopeSession, withForcedTypeCalculator)
}
fun FirClass.scopeForClass(
substitutor: ConeSubstitutor,
useSiteSession: FirSession,
scopeSession: ScopeSession
): FirTypeScope = scopeForClassImpl(
substitutor, useSiteSession, scopeSession,
skipPrivateMembers = false,
classFirDispatchReceiver = this,
// TODO: why it's always false?
isFromExpectClass = false
)
fun ConeKotlinType.scopeForSupertype(
useSiteSession: FirSession,
scopeSession: ScopeSession,
subClass: FirClass,
): FirTypeScope? {
if (this !is ConeClassLikeType) return null
if (this is ConeClassErrorType) return null
val symbol = lookupTag.toSymbol(useSiteSession)
return if (symbol is FirRegularClassSymbol) {
symbol.fir.scopeForClassImpl(
substitutor(symbol, this, useSiteSession),
useSiteSession,
scopeSession,
skipPrivateMembers = true,
classFirDispatchReceiver = subClass,
isFromExpectClass = (subClass as? FirRegularClass)?.isExpect == true
)
} else {
null
}
}
private fun substitutor(symbol: FirRegularClassSymbol, type: ConeClassLikeType, useSiteSession: FirSession): ConeSubstitutor {
if (type.typeArguments.isEmpty()) return ConeSubstitutor.Empty
val originalSubstitution = createSubstitution(symbol.fir.typeParameters, type, useSiteSession)
return substitutorByMap(originalSubstitution, useSiteSession)
}
private fun FirClass.scopeForClassImpl(
substitutor: ConeSubstitutor,
useSiteSession: FirSession,
scopeSession: ScopeSession,
skipPrivateMembers: Boolean,
classFirDispatchReceiver: FirClass,
isFromExpectClass: Boolean
): FirTypeScope {
val basicScope = unsubstitutedScope(useSiteSession, scopeSession, withForcedTypeCalculator = false)
if (substitutor == ConeSubstitutor.Empty) return basicScope
val key = ConeSubstitutionScopeKey(classFirDispatchReceiver.symbol.toLookupTag(), isFromExpectClass, substitutor)
return scopeSession.getOrBuild(
this, key
) {
FirClassSubstitutionScope(
useSiteSession, basicScope, key, substitutor, classFirDispatchReceiver.defaultType(),
skipPrivateMembers, makeExpect = isFromExpectClass
)
}
}
val FirSession.kotlinScopeProvider: FirKotlinScopeProvider by FirSession.sessionComponentAccessor()
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2021 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.scopes
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
interface FirOverrideChecker {
fun isOverriddenFunction(
overrideCandidate: FirSimpleFunction,
baseDeclaration: FirSimpleFunction
): Boolean
fun isOverriddenProperty(
overrideCandidate: FirCallableDeclaration, // NB: in Java it can be a function which overrides accessor
baseDeclaration: FirProperty
): Boolean
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2021 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.scopes
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
import org.jetbrains.kotlin.name.Name
abstract class FirTypeParameterScope : FirContainingNamesAwareScope() {
abstract val typeParameters: Map<Name, List<FirTypeParameter>>
override fun processClassifiersByNameWithSubstitution(
name: Name,
processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit
) {
val matchedTypeParameters = typeParameters[name] ?: return
matchedTypeParameters.forEach { processor(it.symbol, ConeSubstitutor.Empty) }
}
override fun getCallableNames(): Set<Name> = emptySet()
override fun getClassifierNames(): Set<Name> = typeParameters.keys
}
@@ -0,0 +1,17 @@
/*
* Copyright 2010-2021 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.scopes
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
fun ConeClassLikeLookupTag.getNestedClassifierScope(session: FirSession, scopeSession: ScopeSession): FirContainingNamesAwareScope? {
val klass = toSymbol(session)?.fir as? FirRegularClass ?: return null
return klass.scopeProvider.getNestedClassifierScope(klass, session, scopeSession)
}
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.scopes.FirOverrideChecker
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
abstract class AbstractFirOverrideScope(
val session: FirSession,
protected val overrideChecker: FirOverrideChecker
) : FirTypeScope() {
//base symbol as key, overridden as value
val overrideByBase = mutableMapOf<FirCallableSymbol<*>, FirCallableSymbol<*>?>()
private fun isOverriddenFunction(overrideCandidate: FirSimpleFunction, baseDeclaration: FirSimpleFunction): Boolean {
return overrideChecker.isOverriddenFunction(overrideCandidate, baseDeclaration)
}
private fun isOverriddenProperty(overrideCandidate: FirCallableDeclaration, baseDeclaration: FirProperty): Boolean {
return overrideChecker.isOverriddenProperty(overrideCandidate, baseDeclaration)
}
protected fun similarFunctionsOrBothProperties(
overrideCandidate: FirCallableDeclaration,
baseDeclaration: FirCallableDeclaration
): Boolean {
return when (overrideCandidate) {
is FirSimpleFunction -> when (baseDeclaration) {
is FirSimpleFunction -> isOverriddenFunction(overrideCandidate, baseDeclaration)
is FirProperty -> isOverriddenProperty(overrideCandidate, baseDeclaration)
else -> false
}
is FirConstructor -> false
is FirProperty -> baseDeclaration is FirProperty && isOverriddenProperty(overrideCandidate, baseDeclaration)
is FirField -> baseDeclaration is FirField
else -> error("Unknown fir callable type: $overrideCandidate, $baseDeclaration")
}
}
// Receiver is super-type function here
protected open fun FirCallableSymbol<*>.getOverridden(overrideCandidates: Set<FirCallableSymbol<*>>): FirCallableSymbol<*>? {
if (overrideByBase.containsKey(this)) return overrideByBase[this]
val baseDeclaration = (this as FirBasedSymbol<*>).fir as FirCallableDeclaration
val override = overrideCandidates.firstOrNull {
val overrideCandidate = (it as FirBasedSymbol<*>).fir as FirCallableDeclaration
baseDeclaration.modality != Modality.FINAL && similarFunctionsOrBothProperties(overrideCandidate, baseDeclaration)
} // TODO: two or more overrides for one fun?
overrideByBase[this] = override
return override
}
}
@@ -0,0 +1,105 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.scopes.FirOverrideChecker
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.name.Name
abstract class AbstractFirUseSiteMemberScope(
session: FirSession,
overrideChecker: FirOverrideChecker,
protected val superTypesScope: FirTypeScope,
protected val declaredMemberScope: FirContainingNamesAwareScope
) : AbstractFirOverrideScope(session, overrideChecker) {
private val functions = hashMapOf<Name, Collection<FirNamedFunctionSymbol>>()
val directOverriddenFunctions = hashMapOf<FirNamedFunctionSymbol, Collection<FirNamedFunctionSymbol>>()
protected val directOverriddenProperties = hashMapOf<FirPropertySymbol, MutableList<FirPropertySymbol>>()
private val callableNamesCached by lazy(LazyThreadSafetyMode.PUBLICATION) {
declaredMemberScope.getCallableNames() + superTypesScope.getCallableNames()
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
functions.getOrPut(name) {
doProcessFunctions(name)
}.forEach {
processor(it)
}
}
private fun doProcessFunctions(
name: Name
): Collection<FirNamedFunctionSymbol> = mutableListOf<FirNamedFunctionSymbol>().apply {
val overrideCandidates = mutableSetOf<FirFunctionSymbol<*>>()
declaredMemberScope.processFunctionsByName(name) { symbol ->
if (symbol.isStatic) return@processFunctionsByName
val directOverridden = computeDirectOverridden(symbol)
this@AbstractFirUseSiteMemberScope.directOverriddenFunctions[symbol] = directOverridden
overrideCandidates += symbol
add(symbol)
}
superTypesScope.processFunctionsByName(name) {
val overriddenBy = it.getOverridden(overrideCandidates)
if (overriddenBy == null) {
add(it)
}
}
}
private fun computeDirectOverridden(symbol: FirNamedFunctionSymbol): Collection<FirNamedFunctionSymbol> {
val result = mutableListOf<FirNamedFunctionSymbol>()
val firSimpleFunction = symbol.fir
superTypesScope.processFunctionsByName(symbol.callableId.callableName) { superSymbol ->
if (overrideChecker.isOverriddenFunction(firSimpleFunction, superSymbol.fir)) {
result.add(superSymbol)
}
}
return result
}
override fun processDirectOverriddenFunctionsWithBaseScope(
functionSymbol: FirNamedFunctionSymbol,
processor: (FirNamedFunctionSymbol, FirTypeScope) -> ProcessorAction
): ProcessorAction =
//directOverriddenFunctions might be not filled for functionSymbol if it is not from processFunctionsByName call
doProcessDirectOverriddenCallables(
functionSymbol, processor, directOverriddenFunctions, superTypesScope,
FirTypeScope::processDirectOverriddenFunctionsWithBaseScope
)
override fun processDirectOverriddenPropertiesWithBaseScope(
propertySymbol: FirPropertySymbol,
processor: (FirPropertySymbol, FirTypeScope) -> ProcessorAction
): ProcessorAction =
doProcessDirectOverriddenCallables(
propertySymbol, processor, directOverriddenProperties, superTypesScope,
FirTypeScope::processDirectOverriddenPropertiesWithBaseScope
)
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
declaredMemberScope.processClassifiersByNameWithSubstitution(name, processor)
superTypesScope.processClassifiersByNameWithSubstitution(name, processor)
}
override fun processDeclaredConstructors(processor: (FirConstructorSymbol) -> Unit) {
declaredMemberScope.processDeclaredConstructors(processor)
}
override fun getCallableNames(): Set<Name> = callableNamesCached
override fun getClassifierNames(): Set<Name> {
return declaredMemberScope.getClassifierNames() + superTypesScope.getClassifierNames()
}
}
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
enum class DefaultImportPriority {
HIGH {
override fun getAllDefaultImports(
platformDependentAnalyzerServices: PlatformDependentAnalyzerServices?,
languageVersionSettings: LanguageVersionSettings
): List<ImportPath>? =
platformDependentAnalyzerServices?.getDefaultImports(languageVersionSettings, includeLowPriorityImports = false)
},
LOW {
override fun getAllDefaultImports(
platformDependentAnalyzerServices: PlatformDependentAnalyzerServices?,
languageVersionSettings: LanguageVersionSettings
): List<ImportPath>? =
platformDependentAnalyzerServices?.defaultLowPriorityImports
};
abstract fun getAllDefaultImports(
platformDependentAnalyzerServices: PlatformDependentAnalyzerServices?,
languageVersionSettings: LanguageVersionSettings
): List<ImportPath>?
}
@@ -0,0 +1,137 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirResolvedImport
import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.moduleVisibilityChecker
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolvedForCalls
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.annotations.JVM_THROWS_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.annotations.KOTLIN_NATIVE_THROWS_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.annotations.KOTLIN_THROWS_ANNOTATION_FQ_NAME
enum class FirImportingScopeFilter {
ALL, INVISIBLE_CLASSES, MEMBERS_AND_VISIBLE_CLASSES;
fun check(symbol: FirClassLikeSymbol<*>, session: FirSession): Boolean {
if (this == ALL) return true
// TODO: also check DeprecationLevel.HIDDEN and required Kotlin version
val fir = symbol.fir
if (fir !is FirMemberDeclaration) return false
val isVisible = when (fir.status.visibility) {
// When importing from the same module, status may be unknown because the status resolver depends on super types
// to determine visibility for functions, so it may not have finished yet. Since we only care about classes,
// though, "unknown" will always become public anyway.
Visibilities.Unknown -> true
Visibilities.Internal ->
symbol.fir.moduleData == session.moduleData || session.moduleVisibilityChecker?.isInFriendModule(fir) == true
// All non-`internal` visibilities are either even more restrictive (e.g. `private`) or must not
// be checked in imports (e.g. `protected` may be valid in some use sites).
else -> !fir.status.visibility.mustCheckInImports()
}
return isVisible == (this == MEMBERS_AND_VISIBLE_CLASSES)
}
}
abstract class FirAbstractImportingScope(
session: FirSession,
protected val scopeSession: ScopeSession,
protected val filter: FirImportingScopeFilter,
lookupInFir: Boolean
) : FirAbstractProviderBasedScope(session, lookupInFir) {
private val FirClassLikeSymbol<*>.fullyExpandedSymbol: FirClassSymbol<*>?
get() = when (this) {
is FirTypeAliasSymbol -> fir.expandedConeType?.lookupTag?.toSymbol(session)?.fullyExpandedSymbol
is FirClassSymbol<*> -> this
}
private fun FirClassSymbol<*>.getStaticsScope(): FirContainingNamesAwareScope? =
if (fir.classKind == ClassKind.OBJECT) {
FirObjectImportedCallableScope(
classId, fir.unsubstitutedScope(session, scopeSession, withForcedTypeCalculator = false)
)
} else {
fir.scopeProvider.getStaticScope(fir, session, scopeSession)
}
fun getStaticsScope(classId: ClassId): FirContainingNamesAwareScope? =
provider.getClassLikeSymbolByClassId(classId)?.fullyExpandedSymbol?.getStaticsScope()
protected fun findSingleClassifierSymbolByName(name: Name?, imports: List<FirResolvedImport>): FirClassLikeSymbol<*>? {
var result: FirClassLikeSymbol<*>? = null
for (import in imports) {
val importedName = name ?: import.importedName ?: continue
val classId = import.resolvedParentClassId?.createNestedClassId(importedName)
?: ClassId.topLevel(import.packageFqName.child(importedName))
val symbol = provider.getClassLikeSymbolByClassId(classId) ?: continue
if (!filter.check(symbol, session)) continue
result = when {
result == null || result == symbol -> symbol
// Importing multiple versions of the same type is normally an ambiguity, but in the case of `kotlin.Throws`,
// it should take precedence over platform-specific variants. This is lifted directly from `LazyImportScope`
// from the old backend; most likely `Throws` predates expect-actual, and this is a backwards compatibility hack.
// TODO: remove redundant versions of `Throws` from the standard library
result.classId.isJvmOrNativeThrows && symbol.classId.isCommonThrows -> symbol
result.classId.isCommonThrows && symbol.classId.isJvmOrNativeThrows -> result
// TODO: if there is an ambiguity at this scope, further scopes should not be checked.
// Doing otherwise causes KT-39073. Also, returning null here instead of an error symbol
// or something produces poor quality diagnostics ("unresolved name" rather than "ambiguity").
else -> return null
}
}
return result
}
protected fun processFunctionsByName(name: Name?, imports: List<FirResolvedImport>, processor: (FirNamedFunctionSymbol) -> Unit) {
if (filter == FirImportingScopeFilter.INVISIBLE_CLASSES) return
for (import in imports) {
val importedName = name ?: import.importedName ?: continue
val staticsScope = import.resolvedParentClassId?.let(::getStaticsScope)
if (staticsScope != null) {
staticsScope.processFunctionsByName(importedName, processor)
} else if (importedName.isSpecial || importedName.identifier.isNotEmpty()) {
for (symbol in provider.getTopLevelFunctionSymbols(import.packageFqName, importedName)) {
symbol.ensureResolvedForCalls()
processor(symbol)
}
}
}
}
protected fun processPropertiesByName(name: Name?, imports: List<FirResolvedImport>, processor: (FirVariableSymbol<*>) -> Unit) {
if (filter == FirImportingScopeFilter.INVISIBLE_CLASSES) return
for (import in imports) {
val importedName = name ?: import.importedName ?: continue
val staticsScope = import.resolvedParentClassId?.let(::getStaticsScope)
if (staticsScope != null) {
staticsScope.processPropertiesByName(importedName, processor)
} else if (importedName.isSpecial || importedName.identifier.isNotEmpty()) {
for (symbol in provider.getTopLevelPropertySymbols(import.packageFqName, importedName)) {
symbol.ensureResolvedForCalls()
processor(symbol)
}
}
}
}
}
private val ClassId.isJvmOrNativeThrows: Boolean
get() = asSingleFqName().let { it == JVM_THROWS_ANNOTATION_FQ_NAME || it == KOTLIN_NATIVE_THROWS_ANNOTATION_FQ_NAME }
private val ClassId.isCommonThrows: Boolean
get() = asSingleFqName() == KOTLIN_THROWS_ANNOTATION_FQ_NAME
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.scopes.FirOverrideChecker
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
abstract class FirAbstractOverrideChecker : FirOverrideChecker {
protected abstract fun buildTypeParametersSubstitutorIfCompatible(
overrideCandidate: FirCallableDeclaration,
baseDeclaration: FirCallableDeclaration
): ConeSubstitutor?
}
fun buildSubstitutorForOverridesCheck(
overrideCandidate: FirCallableDeclaration,
baseDeclaration: FirCallableDeclaration,
useSiteSession: FirSession
): ConeSubstitutor? {
if (overrideCandidate.typeParameters.size != baseDeclaration.typeParameters.size) return null
if (baseDeclaration.typeParameters.isEmpty()) return ConeSubstitutor.Empty
val types = baseDeclaration.typeParameters.map {
ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), false)
}
return substitutorByMap(overrideCandidate.typeParameters.map { it.symbol }.zip(types).toMap(), useSiteSession)
}
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirCompositeSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirDependenciesSymbolProviderImpl
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.scopes.FirScope
abstract class FirAbstractProviderBasedScope(val session: FirSession, lookupInFir: Boolean = true) :
FirScope() {
val provider = when (val symbolProvider = session.symbolProvider) {
is FirCompositeSymbolProvider -> symbolProvider.takeIf { !lookupInFir }?.providers?.find {
it is FirDependenciesSymbolProviderImpl
} ?: symbolProvider
else -> symbolProvider
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirResolvedImport
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.name.Name
abstract class FirAbstractSimpleImportingScope(
session: FirSession,
scopeSession: ScopeSession
) : FirAbstractImportingScope(session, scopeSession, FirImportingScopeFilter.ALL, lookupInFir = true) {
// TODO try to hide this
abstract val simpleImports: Map<Name, List<FirResolvedImport>>
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
val imports = simpleImports[name] ?: return
val symbol = findSingleClassifierSymbolByName(null, imports) ?: return
processor(symbol, ConeSubstitutor.Empty)
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
val imports = simpleImports[name] ?: return
processFunctionsByName(null, imports, processor)
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
val imports = simpleImports[name] ?: return
processPropertiesByName(null, imports, processor)
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirResolvedImport
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.name.Name
abstract class FirAbstractStarImportingScope(
session: FirSession,
scopeSession: ScopeSession,
filter: FirImportingScopeFilter,
lookupInFir: Boolean
) : FirAbstractImportingScope(session, scopeSession, filter, lookupInFir) {
// TODO try to hide this
abstract val starImports: List<FirResolvedImport>
private val absentClassifierNames = mutableSetOf<Name>()
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
if ((!name.isSpecial && name.identifier.isEmpty()) || starImports.isEmpty() || name in absentClassifierNames) {
return
}
val symbol = findSingleClassifierSymbolByName(name, starImports)
if (symbol != null) {
processor(symbol, ConeSubstitutor.Empty)
} else {
absentClassifierNames += name
}
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) =
processFunctionsByName(name, starImports, processor)
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) =
processPropertiesByName(name, starImports, processor)
}
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isSynthetic
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
abstract class FirClassDeclaredMemberScope : FirContainingNamesAwareScope()
class FirClassDeclaredMemberScopeImpl(
val useSiteSession: FirSession,
klass: FirClass,
useLazyNestedClassifierScope: Boolean = false,
existingNames: List<Name>? = null,
symbolProvider: FirSymbolProvider? = null
) : FirClassDeclaredMemberScope() {
private val nestedClassifierScope: FirContainingNamesAwareScope? = if (useLazyNestedClassifierScope) {
lazyNestedClassifierScope(klass.symbol.classId, existingNames!!, symbolProvider!!)
} else {
useSiteSession.nestedClassifierScope(klass)
}
private val callablesIndex: Map<Name, List<FirCallableSymbol<*>>> = run {
val result = mutableMapOf<Name, MutableList<FirCallableSymbol<*>>>()
loop@ for (declaration in klass.declarations) {
if (declaration is FirCallableDeclaration) {
val name = when (declaration) {
is FirConstructor -> SpecialNames.INIT
is FirVariable -> if (declaration.isSynthetic) continue@loop else declaration.name
is FirSimpleFunction -> declaration.name
else -> continue@loop
}
result.getOrPut(name) { mutableListOf() } += declaration.symbol
}
}
result
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
if (name == SpecialNames.INIT) return
processCallables(name, processor)
}
override fun processDeclaredConstructors(processor: (FirConstructorSymbol) -> Unit) {
processCallables(SpecialNames.INIT, processor)
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
processCallables(name, processor)
}
private inline fun <reified D : FirCallableSymbol<*>> processCallables(
name: Name,
processor: (D) -> Unit
) {
val symbols = callablesIndex[name] ?: emptyList()
for (symbol in symbols) {
if (symbol is D) {
processor(symbol)
}
}
}
override fun processClassifiersByNameWithSubstitution(
name: Name,
processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit
) {
nestedClassifierScope?.processClassifiersByNameWithSubstitution(name, processor)
}
override fun getCallableNames(): Set<Name> {
return callablesIndex.keys
}
override fun getClassifierNames(): Set<Name> {
return nestedClassifierScope?.getClassifierNames().orEmpty()
}
}
@@ -0,0 +1,372 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.caches.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.dispatchReceiverClassOrNull
import org.jetbrains.kotlin.fir.originalForSubstitutionOverride
import org.jetbrains.kotlin.fir.resolve.ScopeSessionKey
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.chain
import org.jetbrains.kotlin.fir.scopes.FakeOverrideSubstitution
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.runIf
class FirClassSubstitutionScope(
private val session: FirSession,
private val useSiteMemberScope: FirTypeScope,
key: ScopeSessionKey<*, *>,
private val substitutor: ConeSubstitutor,
private val dispatchReceiverTypeForSubstitutedMembers: ConeClassLikeType,
private val skipPrivateMembers: Boolean,
private val makeExpect: Boolean = false
) : FirTypeScope() {
companion object {
private val FirVariableSymbol<*>.isOverridable: Boolean
get() = when (this) {
is FirPropertySymbol,
is FirFieldSymbol,
is FirAccessorSymbol -> true
else -> false
}
}
private val substitutionOverrideCache = session.substitutionOverrideStorage.substitutionOverrideCacheByScope.getValue(key, null)
private val newOwnerClassId = dispatchReceiverTypeForSubstitutedMembers.lookupTag.classId
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
useSiteMemberScope.processFunctionsByName(name) process@{ original ->
val function = substitutionOverrideCache.overridesForFunctions.getValue(original, this)
processor(function)
}
return super.processFunctionsByName(name, processor)
}
override fun processDirectOverriddenFunctionsWithBaseScope(
functionSymbol: FirNamedFunctionSymbol,
processor: (FirNamedFunctionSymbol, FirTypeScope) -> ProcessorAction
): ProcessorAction =
processDirectOverriddenWithBaseScope(
functionSymbol,
processor,
FirTypeScope::processDirectOverriddenFunctionsWithBaseScope,
) { it in substitutionOverrideCache.overridesForFunctions }
private inline fun <reified D : FirCallableSymbol<*>> processDirectOverriddenWithBaseScope(
callableSymbol: D,
noinline processor: (D, FirTypeScope) -> ProcessorAction,
processDirectOverriddenCallablesWithBaseScope: FirTypeScope.(D, ((D, FirTypeScope) -> ProcessorAction)) -> ProcessorAction,
originalInCache: (D) -> Boolean
): ProcessorAction {
val original = callableSymbol.originalForSubstitutionOverride?.takeIf { originalInCache(it) }
?: return useSiteMemberScope.processDirectOverriddenCallablesWithBaseScope(callableSymbol, processor)
if (original != callableSymbol) {
if (!processor(original, useSiteMemberScope)) return ProcessorAction.STOP
}
return useSiteMemberScope.processDirectOverriddenCallablesWithBaseScope(original, processor)
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
return useSiteMemberScope.processPropertiesByName(name) process@{ original ->
val symbol = if (original.isOverridable) {
substitutionOverrideCache.overridesForVariables.getValue(original, this)
} else {
original
}
processor(symbol)
}
}
override fun processDirectOverriddenPropertiesWithBaseScope(
propertySymbol: FirPropertySymbol,
processor: (FirPropertySymbol, FirTypeScope) -> ProcessorAction
): ProcessorAction =
processDirectOverriddenWithBaseScope(
propertySymbol, processor, FirTypeScope::processDirectOverriddenPropertiesWithBaseScope,
) { it in substitutionOverrideCache.overridesForVariables }
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
useSiteMemberScope.processClassifiersByNameWithSubstitution(name) { symbol, substitutor ->
processor(symbol, substitutor.chain(this.substitutor))
}
}
private fun ConeKotlinType.substitute(): ConeKotlinType? {
return substitutor.substituteOrNull(this)
}
private fun ConeKotlinType.substitute(substitutor: ConeSubstitutor): ConeKotlinType? {
return substitutor.substituteOrNull(this)
}
fun createSubstitutionOverrideFunction(original: FirNamedFunctionSymbol): FirNamedFunctionSymbol {
if (substitutor == ConeSubstitutor.Empty) return original
val member = original.fir
if (skipPrivateMembers && member.visibility == Visibilities.Private) return original
val (newTypeParameters, newDispatchReceiverType, newReceiverType, newReturnType, newSubstitutor, fakeOverrideSubstitution) = createSubstitutedData(
member
)
val newParameterTypes = member.valueParameters.map {
it.returnTypeRef.coneType.substitute(newSubstitutor)
}
if (newReceiverType == null &&
newReturnType == null &&
newParameterTypes.all { it == null } &&
newTypeParameters === member.typeParameters &&
fakeOverrideSubstitution == null
) {
if (original.dispatchReceiverType?.substitute(substitutor) != null) {
return FirFakeOverrideGenerator.createSubstitutionOverrideFunction(
session,
member,
original,
newDispatchReceiverType ?: dispatchReceiverTypeForSubstitutedMembers,
derivedClassId = newOwnerClassId,
isExpect = makeExpect,
)
}
return original
}
/*
* Member functions can't capture type parameters, so
* it's safe to cast newTypeParameters to List<FirTypeParameter>
*/
@Suppress("UNCHECKED_CAST")
return FirFakeOverrideGenerator.createSubstitutionOverrideFunction(
session,
member,
original,
newDispatchReceiverType ?: dispatchReceiverTypeForSubstitutedMembers,
newReceiverType,
newReturnType,
newParameterTypes,
newTypeParameters as List<FirTypeParameter>,
newOwnerClassId,
makeExpect,
fakeOverrideSubstitution
)
}
fun createSubstitutionOverrideConstructor(original: FirConstructorSymbol): FirConstructorSymbol {
if (substitutor == ConeSubstitutor.Empty) return original
val constructor = original.fir
val (newTypeParameters, _, _, newReturnType, newSubstitutor, fakeOverrideSubstitution) = createSubstitutedData(constructor)
// If constructor has a dispatch receiver, it should be an inner class' constructor.
// It means that we need to substitute its dispatcher as every other type,
// instead of using dispatchReceiverTypeForSubstitutedMembers
val newDispatchReceiverType = original.dispatchReceiverType?.substitute(substitutor)
val newParameterTypes = constructor.valueParameters.map {
it.returnTypeRef.coneType.substitute(newSubstitutor)
}
if (newReturnType == null && newParameterTypes.all { it == null } && newTypeParameters === constructor.typeParameters) {
return original
}
return FirFakeOverrideGenerator.createSubstitutionOverrideConstructor(
FirConstructorSymbol(original.callableId),
session,
constructor,
newDispatchReceiverType,
newReturnType,
newParameterTypes,
newTypeParameters,
makeExpect,
fakeOverrideSubstitution
).symbol
}
fun createSubstitutionOverrideProperty(original: FirPropertySymbol): FirPropertySymbol {
if (substitutor == ConeSubstitutor.Empty) return original
val member = original.fir
if (skipPrivateMembers && member.visibility == Visibilities.Private) return original
val (newTypeParameters, newDispatchReceiverType, newReceiverType, newReturnType, _, fakeOverrideSubstitution) = createSubstitutedData(
member
)
if (newReceiverType == null &&
newReturnType == null &&
newTypeParameters === member.typeParameters &&
fakeOverrideSubstitution == null
) {
if (original.dispatchReceiverType?.substitute(substitutor) != null) {
return FirFakeOverrideGenerator.createSubstitutionOverrideProperty(
session,
member,
original,
newDispatchReceiverType ?: dispatchReceiverTypeForSubstitutedMembers,
derivedClassId = newOwnerClassId,
isExpect = makeExpect,
)
}
return original
}
@Suppress("UNCHECKED_CAST")
return FirFakeOverrideGenerator.createSubstitutionOverrideProperty(
session,
member,
original,
newDispatchReceiverType ?: dispatchReceiverTypeForSubstitutedMembers,
newReceiverType,
newReturnType,
newTypeParameters as List<FirTypeParameter>,
newOwnerClassId,
makeExpect,
fakeOverrideSubstitution
)
}
private data class SubstitutedData(
val typeParameters: List<FirTypeParameterRef>,
val dispatchReceiverType: ConeKotlinType?,
val receiverType: ConeKotlinType?,
val returnType: ConeKotlinType?,
val substitutor: ConeSubstitutor,
val fakeOverrideSubstitution: FakeOverrideSubstitution?
)
private fun createSubstitutedData(member: FirCallableDeclaration): SubstitutedData {
val (newTypeParameters, substitutor) = FirFakeOverrideGenerator.createNewTypeParametersAndSubstitutor(
session,
member as FirTypeParameterRefsOwner,
substitutor,
forceTypeParametersRecreation = dispatchReceiverTypeForSubstitutedMembers.lookupTag != member.dispatchReceiverClassOrNull()
)
val receiverType = member.receiverTypeRef?.coneType
val newReceiverType = receiverType?.substitute(substitutor)
val newDispatchReceiverType = dispatchReceiverTypeForSubstitutedMembers.substitute(substitutor)
member.symbol.ensureResolved(FirResolvePhase.STATUS)
val returnType = member.returnTypeRef.coneTypeSafe<ConeKotlinType>()
val fakeOverrideSubstitution = runIf(returnType == null) { FakeOverrideSubstitution(substitutor, member.symbol) }
val newReturnType = returnType?.substitute(substitutor)
return SubstitutedData(
newTypeParameters,
newDispatchReceiverType,
newReceiverType,
newReturnType,
substitutor,
fakeOverrideSubstitution
)
}
fun createSubstitutionOverrideField(original: FirFieldSymbol): FirFieldSymbol {
if (substitutor == ConeSubstitutor.Empty) return original
val member = original.fir
if (skipPrivateMembers && member.visibility == Visibilities.Private) return original
member.symbol.ensureResolved(FirResolvePhase.STATUS)
val returnType = member.returnTypeRef.coneTypeSafe<ConeKotlinType>()
// TODO: do we have fields with implicit type?
val newReturnType = returnType?.substitute() ?: return original
return FirFakeOverrideGenerator.createSubstitutionOverrideField(session, member, original, newReturnType, newOwnerClassId)
}
fun createSubstitutionOverrideAccessor(original: FirAccessorSymbol): FirAccessorSymbol {
if (substitutor == ConeSubstitutor.Empty) return original
val member = original.fir as FirSyntheticProperty
if (skipPrivateMembers && member.visibility == Visibilities.Private) return original
member.symbol.ensureResolved(FirResolvePhase.STATUS)
val returnType = member.returnTypeRef.coneTypeSafe<ConeKotlinType>()
val fakeOverrideSubstitution = runIf(returnType == null) { FakeOverrideSubstitution(substitutor, original) }
val newReturnType = returnType?.substitute()
val newGetterParameterTypes = member.getter.valueParameters.map {
it.returnTypeRef.coneType.substitute()
}
val newSetterParameterTypes = member.setter?.valueParameters?.map {
it.returnTypeRef.coneType.substitute()
}.orEmpty()
if (original.dispatchReceiverType?.substitute(substitutor) == null &&
newReturnType == null &&
newGetterParameterTypes.all { it == null } &&
newSetterParameterTypes.all { it == null }
) {
return original
}
return FirFakeOverrideGenerator.createSubstitutionOverrideAccessor(
session,
member,
original,
substitutor.substituteOrSelf(dispatchReceiverTypeForSubstitutedMembers),
newReturnType,
newGetterParameterTypes,
newSetterParameterTypes,
fakeOverrideSubstitution
)
}
override fun processDeclaredConstructors(processor: (FirConstructorSymbol) -> Unit) {
useSiteMemberScope.processDeclaredConstructors process@{ original ->
val constructor = substitutionOverrideCache.overridesForConstructors.getValue(original, this)
processor(constructor)
}
}
override fun getCallableNames(): Set<Name> {
return useSiteMemberScope.getCallableNames()
}
override fun getClassifierNames(): Set<Name> {
return useSiteMemberScope.getClassifierNames()
}
}
class FirSubstitutionOverrideStorage(val session: FirSession) : FirSessionComponent {
private val cachesFactory = session.firCachesFactory
val substitutionOverrideCacheByScope: FirCache<ScopeSessionKey<*, *>, SubstitutionOverrideCache, Nothing?> =
cachesFactory.createCache { _ -> SubstitutionOverrideCache(session.firCachesFactory) }
class SubstitutionOverrideCache(cachesFactory: FirCachesFactory) {
val overridesForFunctions: FirCache<FirNamedFunctionSymbol, FirNamedFunctionSymbol, FirClassSubstitutionScope> =
cachesFactory.createCache { original, scope -> scope.createSubstitutionOverrideFunction(original) }
val overridesForConstructors: FirCache<FirConstructorSymbol, FirConstructorSymbol, FirClassSubstitutionScope> =
cachesFactory.createCache { original, scope -> scope.createSubstitutionOverrideConstructor(original) }
val overridesForVariables: FirCache<FirVariableSymbol<*>, FirVariableSymbol<*>, FirClassSubstitutionScope> =
cachesFactory.createCache { original, scope ->
when (original) {
is FirPropertySymbol -> scope.createSubstitutionOverrideProperty(original)
is FirFieldSymbol -> scope.createSubstitutionOverrideField(original)
is FirAccessorSymbol -> scope.createSubstitutionOverrideAccessor(original)
else -> error("symbol $original is not overridable")
}
}
}
}
private val FirSession.substitutionOverrideStorage: FirSubstitutionOverrideStorage by FirSession.sessionComponentAccessor()
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.isStatic
import org.jetbrains.kotlin.name.Name
class FirClassUseSiteMemberScope(
session: FirSession,
superTypesScope: FirTypeScope,
declaredMemberScope: FirContainingNamesAwareScope
) : AbstractFirUseSiteMemberScope(session, FirStandardOverrideChecker(session), superTypesScope, declaredMemberScope) {
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
val seen = mutableSetOf<FirVariableSymbol<*>>()
declaredMemberScope.processPropertiesByName(name) l@{
if (it.isStatic) return@l
if (it is FirPropertySymbol) {
val directOverridden = computeDirectOverridden(it.fir)
this@FirClassUseSiteMemberScope.directOverriddenProperties[it] = directOverridden
}
seen += it
processor(it)
}
superTypesScope.processPropertiesByName(name) {
val overriddenBy = it.getOverridden(seen)
if (overriddenBy == null) {
processor(it)
}
}
}
private fun computeDirectOverridden(property: FirProperty): MutableList<FirPropertySymbol> {
val result = mutableListOf<FirPropertySymbol>()
superTypesScope.processPropertiesByName(property.name) l@{ superSymbol ->
if (superSymbol !is FirPropertySymbol) return@l
if (overrideChecker.isOverriddenProperty(property, superSymbol.fir)) {
result.add(superSymbol)
}
}
return result
}
}
@@ -0,0 +1,134 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.ThreadSafeMutableState
import org.jetbrains.kotlin.fir.caches.FirCache
import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.caches.getValue
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.extensions.declarationGenerators
import org.jetbrains.kotlin.fir.extensions.extensionService
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.scopes.FirNameAwareCompositeScope
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
@ThreadSafeMutableState
class FirDeclaredMemberScopeProvider(val useSiteSession: FirSession) : FirSessionComponent {
private val declaredMemberCache: FirCache<FirClass, FirContainingNamesAwareScope, DeclaredMemberScopeContext> =
useSiteSession.firCachesFactory.createCache { klass, context ->
createDeclaredMemberScope(klass, context.useLazyNestedClassifierScope, context.existingNames, context.symbolProvider)
}
private val nestedClassifierCache: FirCache<FirClass, FirNestedClassifierScope?, Nothing?> =
useSiteSession.firCachesFactory.createCache { klass, _ -> createNestedClassifierScope(klass) }
private val extensions by lazy(LazyThreadSafetyMode.PUBLICATION) { useSiteSession.extensionService.declarationGenerators }
fun declaredMemberScope(
klass: FirClass,
useLazyNestedClassifierScope: Boolean,
existingNames: List<Name>?,
symbolProvider: FirSymbolProvider?
): FirContainingNamesAwareScope {
return declaredMemberCache.getValue(klass, DeclaredMemberScopeContext(useLazyNestedClassifierScope, existingNames, symbolProvider))
}
private data class DeclaredMemberScopeContext(
val useLazyNestedClassifierScope: Boolean,
val existingNames: List<Name>?,
val symbolProvider: FirSymbolProvider?
)
private fun createDeclaredMemberScope(
klass: FirClass,
useLazyNestedClassifierScope: Boolean,
existingNames: List<Name>?,
symbolProvider: FirSymbolProvider?
): FirContainingNamesAwareScope {
return when {
klass.origin.generated -> {
FirGeneratedClassDeclaredMemberScope(useSiteSession, klass, needNestedClassifierScope = true)
}
else -> {
val baseScope = FirClassDeclaredMemberScopeImpl(
useSiteSession,
klass,
useLazyNestedClassifierScope,
existingNames,
symbolProvider
)
if (extensions.any { it.needToGenerateAdditionalMembersInClass(klass) }) {
FirNameAwareCompositeScope(
listOf(baseScope, FirGeneratedClassDeclaredMemberScope(useSiteSession, klass, needNestedClassifierScope = false))
)
} else {
baseScope
}
}
}
}
fun nestedClassifierScope(klass: FirClass): FirNestedClassifierScope? {
return nestedClassifierCache.getValue(klass)
}
private fun createNestedClassifierScope(klass: FirClass): FirNestedClassifierScope? {
return if (klass.origin.generated) {
FirGeneratedClassNestedClassifierScope(klass, useSiteSession)
} else {
val baseScope = FirNestedClassifierScopeImpl(klass, useSiteSession)
if (extensions.any { it.needToGenerateNestedClassifiersInClass(klass) }) {
FirCompositeNestedClassifierScope(
listOf(baseScope, FirGeneratedClassNestedClassifierScope(klass, useSiteSession)),
klass,
useSiteSession
)
} else {
baseScope
}
}.takeUnless { it.isEmpty() }
}
}
fun FirSession.declaredMemberScope(klass: FirClass): FirContainingNamesAwareScope {
return declaredMemberScopeProvider
.declaredMemberScope(klass, useLazyNestedClassifierScope = false, existingNames = null, symbolProvider = null)
}
fun FirSession.declaredMemberScope(klass: FirClassSymbol<*>): FirContainingNamesAwareScope {
return declaredMemberScope(klass.fir)
}
fun FirSession.declaredMemberScopeWithLazyNestedScope(
klass: FirClass,
existingNames: List<Name>,
symbolProvider: FirSymbolProvider
): FirContainingNamesAwareScope {
return declaredMemberScopeProvider
.declaredMemberScope(klass, useLazyNestedClassifierScope = true, existingNames = existingNames, symbolProvider = symbolProvider)
}
fun FirSession.nestedClassifierScope(klass: FirClass): FirNestedClassifierScope? {
return declaredMemberScopeProvider
.nestedClassifierScope(klass)
}
fun lazyNestedClassifierScope(
classId: ClassId,
existingNames: List<Name>,
symbolProvider: FirSymbolProvider
): FirLazyNestedClassifierScope? {
if (existingNames.isEmpty()) return null
return FirLazyNestedClassifierScope(classId, existingNames, symbolProvider)
}
val FirSession.declaredMemberScopeProvider: FirDeclaredMemberScopeProvider by FirSession.sessionComponentAccessor()
@@ -0,0 +1,67 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.config.AnalysisFlags
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.builder.buildImport
import org.jetbrains.kotlin.fir.declarations.builder.buildResolvedImport
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.name.Name
class FirDefaultStarImportingScope(
session: FirSession,
scopeSession: ScopeSession,
filter: FirImportingScopeFilter,
priority: DefaultImportPriority
) : FirAbstractStarImportingScope(
session, scopeSession, filter,
lookupInFir = session.languageVersionSettings.getFlag(AnalysisFlags.allowKotlinPackage)
) {
// TODO: put languageVersionSettings into FirSession?
override val starImports = run {
val analyzerServices = session.moduleData.analyzerServices
val allDefaultImports = priority.getAllDefaultImports(analyzerServices, LanguageVersionSettingsImpl.DEFAULT)
allDefaultImports
?.filter { it.isAllUnder }
?.map {
buildResolvedImport {
delegate = buildImport {
importedFqName = it.fqName
isAllUnder = true
}
packageFqName = it.fqName
}
} ?: emptyList()
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
if (filter == FirImportingScopeFilter.INVISIBLE_CLASSES) return
if (name.isSpecial || name.identifier.isNotEmpty()) {
for (import in starImports) {
for (symbol in provider.getTopLevelFunctionSymbols(import.packageFqName, name)) {
processor(symbol)
}
}
}
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
if (filter == FirImportingScopeFilter.INVISIBLE_CLASSES) return
if (name.isSpecial || name.identifier.isNotEmpty()) {
for (import in starImports) {
for (symbol in provider.getTopLevelPropertySymbols(import.packageFqName, name)) {
processor(symbol)
}
}
}
}
}
@@ -0,0 +1,248 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.scope
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeFlexibleType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isMarkedNullable
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
class FirDelegatedMemberScope(
private val session: FirSession,
private val scopeSession: ScopeSession,
private val containingClass: FirClass,
private val declaredMemberScope: FirContainingNamesAwareScope,
private val delegateFields: List<FirField>,
) : FirContainingNamesAwareScope() {
private val dispatchReceiverType = containingClass.defaultType()
private val overrideChecker = FirStandardOverrideChecker(session)
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
declaredMemberScope.processFunctionsByName(name, processor)
val result = mutableListOf<FirNamedFunctionSymbol>()
for (delegateField in delegateFields) {
collectFunctionsFromSpecificField(delegateField, name, result)
}
result.forEach(processor)
}
private fun buildScope(delegateField: FirField): FirTypeScope? {
delegateField.ensureResolved(FirResolvePhase.TYPES)
return delegateField.returnTypeRef.coneType.scope(session, scopeSession, FakeOverrideTypeCalculator.DoNothing)
}
private fun collectFunctionsFromSpecificField(
delegateField: FirField,
name: Name,
result: MutableList<FirNamedFunctionSymbol>
) {
val scope = buildScope(delegateField) ?: return
scope.processFunctionsByName(name) processor@{ functionSymbol ->
val original = functionSymbol.fir
// KT-6014: If the original is abstract, we still need a delegation
// For example,
// interface IBase { override fun toString(): String }
// object BaseImpl : IBase { override fun toString(): String = ... }
// class Test : IBase by BaseImpl
if (original.isPublicInAny() && original.modality != Modality.ABSTRACT) {
return@processor
}
if (original.modality == Modality.FINAL || original.visibility == Visibilities.Private) {
return@processor
}
if (declaredMemberScope.getFunctions(name).any { overrideChecker.isOverriddenFunction(it.fir, original) }) {
return@processor
}
result.firstOrNull {
overrideChecker.isOverriddenFunction(it.fir, original)
}?.let {
it.fir.multipleDelegatesWithTheSameSignature = true
return@processor
}
val delegatedSymbol =
FirFakeOverrideGenerator.createCopyForFirFunction(
FirNamedFunctionSymbol(
functionSymbol.callableId,
),
original,
session,
FirDeclarationOrigin.Delegated,
newDispatchReceiverType = dispatchReceiverType,
newModality = Modality.OPEN,
).apply {
delegatedWrapperData = DelegatedWrapperData(functionSymbol.fir, containingClass.symbol.toLookupTag(), delegateField)
}.symbol
result += delegatedSymbol
}
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
declaredMemberScope.processPropertiesByName(name, processor)
val result = mutableListOf<FirPropertySymbol>()
for (delegateField in delegateFields) {
collectPropertiesFromSpecificField(delegateField, name, result)
}
result.forEach(processor)
}
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
declaredMemberScope.processClassifiersByNameWithSubstitution(name, processor)
}
override fun processDeclaredConstructors(processor: (FirConstructorSymbol) -> Unit) {
declaredMemberScope.processDeclaredConstructors(processor)
}
private fun collectPropertiesFromSpecificField(
delegateField: FirField,
name: Name,
result: MutableList<FirPropertySymbol>
) {
val scope = buildScope(delegateField) ?: return
scope.processPropertiesByName(name) processor@{ propertySymbol ->
if (propertySymbol !is FirPropertySymbol) {
return@processor
}
val original = propertySymbol.fir
if (original.modality == Modality.FINAL || original.visibility == Visibilities.Private) {
return@processor
}
if (declaredMemberScope.getProperties(name)
.any { it is FirPropertySymbol && overrideChecker.isOverriddenProperty(it.fir, original) }
) {
return@processor
}
result.firstOrNull {
overrideChecker.isOverriddenProperty(it.fir, original)
}?.let {
it.fir.multipleDelegatesWithTheSameSignature = true
return@processor
}
val delegatedSymbol =
FirFakeOverrideGenerator.createCopyForFirProperty(
FirPropertySymbol(
propertySymbol.callableId
),
original,
session,
FirDeclarationOrigin.Delegated,
newModality = Modality.OPEN,
newDispatchReceiverType = dispatchReceiverType,
).apply {
delegatedWrapperData = DelegatedWrapperData(propertySymbol.fir, containingClass.symbol.toLookupTag(), delegateField)
}.symbol
result += delegatedSymbol
}
}
@OptIn(ExperimentalStdlibApi::class)
private val callableNamesLazy: Set<Name> by lazy(LazyThreadSafetyMode.PUBLICATION) {
buildSet {
addAll(declaredMemberScope.getCallableNames())
delegateFields.flatMapTo(this) {
buildScope(it)?.getCallableNames() ?: emptySet()
}
}
}
@OptIn(ExperimentalStdlibApi::class)
private val classifierNamesLazy: Set<Name> by lazy(LazyThreadSafetyMode.PUBLICATION) {
buildSet {
addAll(declaredMemberScope.getClassifierNames())
delegateFields.flatMapTo(this) {
buildScope(it)?.getClassifierNames() ?: emptySet()
}
}
}
override fun getCallableNames(): Set<Name> = callableNamesLazy
override fun getClassifierNames(): Set<Name> = classifierNamesLazy
}
private object MultipleDelegatesWithTheSameSignatureKey : FirDeclarationDataKey()
var FirCallableDeclaration.multipleDelegatesWithTheSameSignature: Boolean? by FirDeclarationDataRegistry.data(
MultipleDelegatesWithTheSameSignatureKey
)
val FirCallableSymbol<*>.multipleDelegatesWithTheSameSignature: Boolean?
get() = fir.multipleDelegatesWithTheSameSignature
private object DelegatedWrapperDataKey : FirDeclarationDataKey()
class DelegatedWrapperData<D : FirCallableDeclaration>(
val wrapped: D,
val containingClass: ConeClassLikeLookupTag,
val delegateField: FirField,
)
var <D : FirCallableDeclaration>
D.delegatedWrapperData: DelegatedWrapperData<D>? by FirDeclarationDataRegistry.data(DelegatedWrapperDataKey)
val <D : FirCallableDeclaration> FirCallableSymbol<out D>.delegatedWrapperData: DelegatedWrapperData<D>?
get() = fir.delegatedWrapperData
// From the definition of function interfaces in the Java specification (pt. 9.8):
// "methods that are members of I that do not have the same signature as any public instance method of the class Object"
// It means that if an interface declares `int hashCode()` then the method won't be taken into account when
// checking if the interface is SAM.
fun FirSimpleFunction.isPublicInAny(): Boolean {
if (name.asString() !in PUBLIC_METHOD_NAMES_IN_ANY) return false
return when (name.asString()) {
"hashCode", "toString" -> valueParameters.isEmpty()
"equals" -> valueParameters.singleOrNull()?.hasTypeOf(StandardClassIds.Any, allowNullable = true) == true
else -> error("Unexpected method name: $name")
}
}
fun FirValueParameter.hasTypeOf(classId: ClassId, allowNullable: Boolean): Boolean {
val classLike = when (val type = returnTypeRef.coneType) {
is ConeClassLikeType -> type
is ConeFlexibleType -> type.upperBound as? ConeClassLikeType ?: return false
else -> return false
}
if (classLike.isMarkedNullable && !allowNullable) return false
return classLike.lookupTag.classId == classId
}
private val PUBLIC_METHOD_NAMES_IN_ANY = setOf("equals", "hashCode", "toString")
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirImport
import org.jetbrains.kotlin.fir.declarations.FirResolvedImport
import org.jetbrains.kotlin.fir.resolve.ScopeSession
class FirExplicitSimpleImportingScope(
imports: List<FirImport>,
session: FirSession,
scopeSession: ScopeSession
) : FirAbstractSimpleImportingScope(session, scopeSession) {
override val simpleImports =
imports.filterIsInstance<FirResolvedImport>()
.filter { !it.isAllUnder && it.importedName != null }
.groupBy { it.aliasName ?: it.importedName!! }
override val scopeOwnerLookupNames: List<String> by lazy(LazyThreadSafetyMode.PUBLICATION) {
simpleImports.values.flatMapTo(LinkedHashSet()) { it.map { it.packageFqName.asString() } }.toList()
}
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirImport
import org.jetbrains.kotlin.fir.declarations.FirResolvedImport
import org.jetbrains.kotlin.fir.resolve.ScopeSession
class FirExplicitStarImportingScope(
imports: List<FirImport>,
session: FirSession,
scopeSession: ScopeSession,
filter: FirImportingScopeFilter
) : FirAbstractStarImportingScope(session, scopeSession, filter, lookupInFir = true) {
override val starImports = imports.filterIsInstance<FirResolvedImport>().filter { it.isAllUnder }
override val scopeOwnerLookupNames: List<String> by lazy(LazyThreadSafetyMode.PUBLICATION) {
starImports.mapTo(LinkedHashSet()) { it.packageFqName.asString() }.toList()
}
}
@@ -0,0 +1,530 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.*
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
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.scopes.FakeOverrideSubstitution
import org.jetbrains.kotlin.fir.scopes.fakeOverrideSubstitution
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildImplicitTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.utils.addToStdlib.runIf
object FirFakeOverrideGenerator {
fun createSubstitutionOverrideFunction(
session: FirSession,
baseFunction: FirSimpleFunction,
baseSymbol: FirNamedFunctionSymbol,
newDispatchReceiverType: ConeKotlinType?,
newReceiverType: ConeKotlinType? = null,
newReturnType: ConeKotlinType? = null,
newParameterTypes: List<ConeKotlinType?>? = null,
newTypeParameters: List<FirTypeParameter>? = null,
derivedClassId: ClassId? = null,
isExpect: Boolean = baseFunction.isExpect,
fakeOverrideSubstitution: FakeOverrideSubstitution? = null
): FirNamedFunctionSymbol {
val symbol = if (derivedClassId == null) {
FirNamedFunctionSymbol(baseSymbol.callableId)
} else {
FirNamedFunctionSymbol(CallableId(derivedClassId, baseFunction.name))
}
createSubstitutionOverrideFunction(
symbol, session, baseFunction, newDispatchReceiverType, newReceiverType, newReturnType,
newParameterTypes, newTypeParameters, isExpect, fakeOverrideSubstitution
)
return symbol
}
private fun createSubstitutionOverrideFunction(
fakeOverrideSymbol: FirNamedFunctionSymbol,
session: FirSession,
baseFunction: FirSimpleFunction,
newDispatchReceiverType: ConeKotlinType?,
newReceiverType: ConeKotlinType?,
newReturnType: ConeKotlinType?,
newParameterTypes: List<ConeKotlinType?>?,
newTypeParameters: List<FirTypeParameter>?,
isExpect: Boolean = baseFunction.isExpect,
fakeOverrideSubstitution: FakeOverrideSubstitution?,
): FirSimpleFunction {
// TODO: consider using here some light-weight functions instead of pseudo-real FirMemberFunctionImpl
// As second alternative, we can invent some light-weight kind of FirRegularClass
return createCopyForFirFunction(
fakeOverrideSymbol,
baseFunction,
session,
FirDeclarationOrigin.SubstitutionOverride,
isExpect,
newDispatchReceiverType,
newParameterTypes,
newTypeParameters,
newReceiverType,
newReturnType,
fakeOverrideSubstitution = fakeOverrideSubstitution
).apply {
originalForSubstitutionOverrideAttr = baseFunction
}
}
fun createCopyForFirFunction(
newSymbol: FirNamedFunctionSymbol,
baseFunction: FirSimpleFunction,
session: FirSession,
origin: FirDeclarationOrigin,
isExpect: Boolean = baseFunction.isExpect,
newDispatchReceiverType: ConeKotlinType?,
newParameterTypes: List<ConeKotlinType?>? = null,
newTypeParameters: List<FirTypeParameter>? = null,
newReceiverType: ConeKotlinType? = null,
newReturnType: ConeKotlinType? = null,
newModality: Modality? = null,
newVisibility: Visibility? = null,
fakeOverrideSubstitution: FakeOverrideSubstitution? = null
): FirSimpleFunction {
return buildSimpleFunction {
source = baseFunction.source
moduleData = session.nullableModuleData ?: baseFunction.moduleData
this.origin = origin
name = baseFunction.name
status = baseFunction.status.copy(isExpect, newModality, newVisibility)
symbol = newSymbol
resolvePhase = baseFunction.resolvePhase
dispatchReceiverType = newDispatchReceiverType
attributes = baseFunction.attributes.copy()
typeParameters += configureAnnotationsTypeParametersAndSignature(
session, baseFunction, newParameterTypes, newTypeParameters, newReceiverType, newReturnType, fakeOverrideSubstitution
).filterIsInstance<FirTypeParameter>()
deprecation = baseFunction.deprecation
}
}
fun createSubstitutionOverrideConstructor(
fakeOverrideSymbol: FirConstructorSymbol,
session: FirSession,
baseConstructor: FirConstructor,
newDispatchReceiverType: ConeKotlinType?,
newReturnType: ConeKotlinType?,
newParameterTypes: List<ConeKotlinType?>?,
newTypeParameters: List<FirTypeParameterRef>?,
isExpect: Boolean,
fakeOverrideSubstitution: FakeOverrideSubstitution?
): FirConstructor {
// TODO: consider using here some light-weight functions instead of pseudo-real FirMemberFunctionImpl
// As second alternative, we can invent some light-weight kind of FirRegularClass
return buildConstructor {
moduleData = session.moduleData
origin = FirDeclarationOrigin.SubstitutionOverride
receiverTypeRef = baseConstructor.receiverTypeRef?.withReplacedConeType(null)
status = baseConstructor.status.copy(isExpect)
symbol = fakeOverrideSymbol
typeParameters += configureAnnotationsTypeParametersAndSignature(
session,
baseConstructor,
newParameterTypes,
newTypeParameters,
newReceiverType = null,
newReturnType,
fakeOverrideSubstitution
)
dispatchReceiverType = newDispatchReceiverType
resolvePhase = baseConstructor.resolvePhase
source = baseConstructor.source
attributes = baseConstructor.attributes.copy()
deprecation = baseConstructor.deprecation
}.apply {
originalForSubstitutionOverrideAttr = baseConstructor
}
}
private fun FirFunctionBuilder.configureAnnotationsTypeParametersAndSignature(
useSiteSession: FirSession,
baseFunction: FirFunction,
newParameterTypes: List<ConeKotlinType?>?,
newTypeParameters: List<FirTypeParameterRef>?,
newReceiverType: ConeKotlinType?,
newReturnType: ConeKotlinType?,
fakeOverrideSubstitution: FakeOverrideSubstitution?
): List<FirTypeParameterRef> {
return when {
baseFunction.typeParameters.isEmpty() -> {
configureAnnotationsAndSignature(
baseFunction,
newParameterTypes,
newReceiverType,
newReturnType,
fakeOverrideSubstitution
)
emptyList()
}
newTypeParameters == null -> {
val (copiedTypeParameters, substitutor) = createNewTypeParametersAndSubstitutor(
useSiteSession, baseFunction, ConeSubstitutor.Empty
)
val copiedParameterTypes = baseFunction.valueParameters.map {
substitutor.substituteOrNull(it.returnTypeRef.coneType)
}
val symbol = baseFunction.symbol
val (copiedReceiverType, possibleReturnType) = substituteReceiverAndReturnType(
baseFunction as FirCallableDeclaration, newReceiverType, newReturnType, substitutor
)
val (copiedReturnType, newFakeOverrideSubstitution) = when (possibleReturnType) {
is Maybe.Value -> possibleReturnType.value to null
else -> null to FakeOverrideSubstitution(substitutor, symbol)
}
configureAnnotationsAndSignature(
baseFunction,
copiedParameterTypes,
copiedReceiverType,
copiedReturnType,
newFakeOverrideSubstitution
)
copiedTypeParameters
}
else -> {
configureAnnotationsAndSignature(
baseFunction,
newParameterTypes,
newReceiverType,
newReturnType,
fakeOverrideSubstitution
)
newTypeParameters
}
}
}
private fun FirFunctionBuilder.configureAnnotationsAndSignature(
baseFunction: FirFunction,
newParameterTypes: List<ConeKotlinType?>?,
newReceiverType: ConeKotlinType?,
newReturnType: ConeKotlinType?,
fakeOverrideSubstitution: FakeOverrideSubstitution?
) {
annotations += baseFunction.annotations
@Suppress("NAME_SHADOWING")
val fakeOverrideSubstitution = fakeOverrideSubstitution ?: runIf(baseFunction.returnTypeRef is FirImplicitTypeRef) {
FakeOverrideSubstitution(ConeSubstitutor.Empty, baseFunction.symbol)
}
if (fakeOverrideSubstitution != null) {
returnTypeRef = buildImplicitTypeRef()
attributes.fakeOverrideSubstitution = fakeOverrideSubstitution
} else {
returnTypeRef = baseFunction.returnTypeRef.withReplacedReturnType(newReturnType)
}
if (this is FirSimpleFunctionBuilder) {
receiverTypeRef = baseFunction.receiverTypeRef?.withReplacedConeType(newReceiverType)
}
valueParameters += baseFunction.valueParameters.zip(
newParameterTypes ?: List(baseFunction.valueParameters.size) { null }
) { valueParameter, newType ->
buildValueParameterCopy(valueParameter) {
origin = FirDeclarationOrigin.SubstitutionOverride
returnTypeRef = valueParameter.returnTypeRef.withReplacedConeType(newType)
symbol = FirValueParameterSymbol(valueParameter.name)
}
}
}
fun createSubstitutionOverrideProperty(
session: FirSession,
baseProperty: FirProperty,
baseSymbol: FirPropertySymbol,
newDispatchReceiverType: ConeKotlinType?,
newReceiverType: ConeKotlinType? = null,
newReturnType: ConeKotlinType? = null,
newTypeParameters: List<FirTypeParameter>? = null,
derivedClassId: ClassId? = null,
isExpect: Boolean = baseProperty.isExpect,
fakeOverrideSubstitution: FakeOverrideSubstitution? = null
): FirPropertySymbol {
val symbol = if (derivedClassId == null) {
FirPropertySymbol(baseSymbol.callableId)
} else {
FirPropertySymbol(CallableId(derivedClassId, baseProperty.name))
}
createCopyForFirProperty(
symbol, baseProperty, session, FirDeclarationOrigin.SubstitutionOverride, isExpect,
newDispatchReceiverType, newTypeParameters, newReceiverType, newReturnType,
fakeOverrideSubstitution = fakeOverrideSubstitution
).apply {
originalForSubstitutionOverrideAttr = baseProperty
}
return symbol
}
fun createCopyForFirProperty(
newSymbol: FirPropertySymbol,
baseProperty: FirProperty,
session: FirSession,
origin: FirDeclarationOrigin,
isExpect: Boolean = baseProperty.isExpect,
newDispatchReceiverType: ConeKotlinType?,
newTypeParameters: List<FirTypeParameter>? = null,
newReceiverType: ConeKotlinType? = null,
newReturnType: ConeKotlinType? = null,
newModality: Modality? = null,
newVisibility: Visibility? = null,
fakeOverrideSubstitution: FakeOverrideSubstitution? = null
): FirProperty {
return buildProperty {
source = baseProperty.source
moduleData = session.moduleData
this.origin = origin
name = baseProperty.name
isVar = baseProperty.isVar
this.symbol = newSymbol
isLocal = false
status = baseProperty.status.copy(isExpect, newModality, newVisibility)
resolvePhase = baseProperty.resolvePhase
dispatchReceiverType = newDispatchReceiverType
attributes = baseProperty.attributes.copy()
typeParameters += configureAnnotationsTypeParametersAndSignature(
session,
baseProperty,
newTypeParameters,
newReceiverType,
newReturnType,
fakeOverrideSubstitution
)
deprecation = baseProperty.deprecation
}
}
private fun FirPropertyBuilder.configureAnnotationsTypeParametersAndSignature(
useSiteSession: FirSession,
baseProperty: FirProperty,
newTypeParameters: List<FirTypeParameter>?,
newReceiverType: ConeKotlinType?,
newReturnType: ConeKotlinType?,
fakeOverrideSubstitution: FakeOverrideSubstitution?
): List<FirTypeParameter> {
return when {
baseProperty.typeParameters.isEmpty() -> {
configureAnnotationsAndSignature(baseProperty, newReceiverType, newReturnType, fakeOverrideSubstitution)
emptyList()
}
newTypeParameters == null -> {
val (copiedTypeParameters, substitutor) = createNewTypeParametersAndSubstitutor(
useSiteSession, baseProperty, ConeSubstitutor.Empty
)
val (copiedReceiverType, possibleReturnType) = substituteReceiverAndReturnType(
baseProperty, newReceiverType, newReturnType, substitutor
)
val (copiedReturnType, newFakeOverrideSubstitution) = when (possibleReturnType) {
is Maybe.Value -> possibleReturnType.value to null
else -> null to FakeOverrideSubstitution(substitutor, baseProperty.symbol)
}
configureAnnotationsAndSignature(baseProperty, copiedReceiverType, copiedReturnType, newFakeOverrideSubstitution)
copiedTypeParameters.filterIsInstance<FirTypeParameter>()
}
else -> {
configureAnnotationsAndSignature(baseProperty, newReceiverType, newReturnType, fakeOverrideSubstitution)
newTypeParameters
}
}
}
private fun substituteReceiverAndReturnType(
baseCallable: FirCallableDeclaration,
newReceiverType: ConeKotlinType?,
newReturnType: ConeKotlinType?,
substitutor: ConeSubstitutor
): Pair<ConeKotlinType?, Maybe<ConeKotlinType?>> {
val copiedReceiverType = newReceiverType?.let {
substitutor.substituteOrNull(it)
} ?: baseCallable.receiverTypeRef?.let {
substitutor.substituteOrNull(it.coneType)
}
val copiedReturnType = newReturnType?.let {
substitutor.substituteOrNull(it)
} ?: baseCallable.returnTypeRef.let {
val coneType = baseCallable.returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: return copiedReceiverType to Maybe.Nothing
substitutor.substituteOrNull(coneType)
}
return copiedReceiverType to Maybe.Value(copiedReturnType)
}
private fun FirPropertyBuilder.configureAnnotationsAndSignature(
baseProperty: FirProperty,
newReceiverType: ConeKotlinType?,
newReturnType: ConeKotlinType?,
fakeOverrideSubstitution: FakeOverrideSubstitution?
) {
annotations += baseProperty.annotations
@Suppress("NAME_SHADOWING")
val fakeOverrideSubstitution = fakeOverrideSubstitution ?: runIf(baseProperty.returnTypeRef is FirImplicitTypeRef) {
FakeOverrideSubstitution(ConeSubstitutor.Empty, baseProperty.symbol)
}
if (fakeOverrideSubstitution != null) {
returnTypeRef = buildImplicitTypeRef()
attributes.fakeOverrideSubstitution = fakeOverrideSubstitution
} else {
returnTypeRef = baseProperty.returnTypeRef.withReplacedReturnType(newReturnType)
}
receiverTypeRef = baseProperty.receiverTypeRef?.withReplacedConeType(newReceiverType)
}
fun createSubstitutionOverrideField(
session: FirSession,
baseField: FirField,
baseSymbol: FirFieldSymbol,
newReturnType: ConeKotlinType?,
derivedClassId: ClassId?
): FirFieldSymbol {
val symbol = FirFieldSymbol(
CallableId(derivedClassId ?: baseSymbol.callableId.classId!!, baseField.name)
)
buildField {
moduleData = session.moduleData
this.symbol = symbol
origin = FirDeclarationOrigin.SubstitutionOverride
returnTypeRef = baseField.returnTypeRef.withReplacedConeType(newReturnType)
source = baseField.source
resolvePhase = baseField.resolvePhase
name = baseField.name
isVar = baseField.isVar
status = baseField.status
resolvePhase = baseField.resolvePhase
annotations += baseField.annotations
attributes = baseField.attributes.copy()
dispatchReceiverType = baseField.dispatchReceiverType
}.apply {
originalForSubstitutionOverrideAttr = baseField
}
return symbol
}
fun createSubstitutionOverrideAccessor(
session: FirSession,
baseProperty: FirSyntheticProperty,
baseSymbol: FirAccessorSymbol,
newDispatchReceiverType: ConeKotlinType?,
newReturnType: ConeKotlinType?,
newGetterParameterTypes: List<ConeKotlinType?>?,
newSetterParameterTypes: List<ConeKotlinType?>?,
fakeOverrideSubstitution: FakeOverrideSubstitution?
): FirAccessorSymbol {
val getterSymbol = FirNamedFunctionSymbol(baseSymbol.accessorId)
val getter = createSubstitutionOverrideFunction(
getterSymbol,
session,
baseProperty.getter.delegate,
newDispatchReceiverType,
newReceiverType = null,
newReturnType,
newGetterParameterTypes,
newTypeParameters = null,
fakeOverrideSubstitution = fakeOverrideSubstitution
)
val setterSymbol = FirNamedFunctionSymbol(baseSymbol.accessorId)
val baseSetter = baseProperty.setter
val setter = if (baseSetter == null) null else createSubstitutionOverrideFunction(
setterSymbol,
session,
baseSetter.delegate,
newDispatchReceiverType,
newReceiverType = null,
StandardClassIds.Unit.constructClassLikeType(emptyArray(), isNullable = false),
newSetterParameterTypes,
newTypeParameters = null,
fakeOverrideSubstitution = fakeOverrideSubstitution
)
return buildSyntheticProperty {
moduleData = session.moduleData
name = baseProperty.name
symbol = FirAccessorSymbol(baseSymbol.callableId, baseSymbol.accessorId)
delegateGetter = getter
delegateSetter = setter
status = baseProperty.status
deprecation = getDeprecationsFromAccessors(getter, setter, session.languageVersionSettings.apiVersion)
}.symbol
}
// Returns a list of type parameters, and a substitutor that should be used for all other types
fun createNewTypeParametersAndSubstitutor(
useSiteSession: FirSession,
member: FirTypeParameterRefsOwner,
substitutor: ConeSubstitutor,
forceTypeParametersRecreation: Boolean = true
): Pair<List<FirTypeParameterRef>, ConeSubstitutor> {
if (member.typeParameters.isEmpty()) return Pair(member.typeParameters, substitutor)
val newTypeParameters = member.typeParameters.map { typeParameterRef ->
val typeParameter = typeParameterRef.symbol.fir
FirTypeParameterBuilder().apply {
source = typeParameter.source
moduleData = typeParameter.moduleData
origin = FirDeclarationOrigin.SubstitutionOverride
resolvePhase = FirResolvePhase.DECLARATIONS
name = typeParameter.name
symbol = FirTypeParameterSymbol()
variance = typeParameter.variance
isReified = typeParameter.isReified
annotations += typeParameter.annotations
}
}
val substitutionMapForNewParameters = member.typeParameters.zip(newTypeParameters).associate { (original, new) ->
Pair(original.symbol, ConeTypeParameterTypeImpl(new.symbol.toLookupTag(), isNullable = false))
}
val additionalSubstitutor = substitutorByMap(substitutionMapForNewParameters, useSiteSession)
var wereChangesInTypeParameters = forceTypeParametersRecreation
for ((newTypeParameter, oldTypeParameter) in newTypeParameters.zip(member.typeParameters)) {
val original = oldTypeParameter.symbol.fir
for (boundTypeRef in original.bounds) {
val typeForBound = boundTypeRef.coneType
val substitutedBound = substitutor.substituteOrNull(typeForBound)
if (substitutedBound != null) {
wereChangesInTypeParameters = true
}
newTypeParameter.bounds +=
buildResolvedTypeRef {
source = boundTypeRef.source
type = additionalSubstitutor.substituteOrSelf(substitutedBound ?: typeForBound)
}
}
}
if (!wereChangesInTypeParameters) return Pair(member.typeParameters, substitutor)
return Pair(
newTypeParameters.map(FirTypeParameterBuilder::build),
ChainedSubstitutor(substitutor, additionalSubstitutor)
)
}
private sealed class Maybe<out A> {
class Value<out A>(val value: A) : Maybe<A>()
object Nothing : Maybe<kotlin.Nothing>()
}
}
@@ -0,0 +1,169 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.caches.FirCache
import org.jetbrains.kotlin.fir.caches.FirLazyValue
import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.caches.getValue
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.declarations.validate
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
import org.jetbrains.kotlin.fir.extensions.declarationGenerators
import org.jetbrains.kotlin.fir.extensions.extensionService
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.runIf
class FirGeneratedClassDeclaredMemberScope(
val useSiteSession: FirSession,
val firClass: FirClass,
needNestedClassifierScope: Boolean
) : FirClassDeclaredMemberScope() {
private val extensions: List<FirDeclarationGenerationExtension> = firClass.findGeneratedExtensions(useSiteSession) {
needToGenerateAdditionalMembersInClass(it)
}
private val nestedClassifierScope: FirNestedClassifierScope? = runIf(needNestedClassifierScope) {
useSiteSession.nestedClassifierScope(firClass)
}
private val firCachesFactory = useSiteSession.firCachesFactory
// ------------------------------------------ caches ------------------------------------------
private val functionCache: FirCache<Name, List<FirNamedFunctionSymbol>, Nothing?> = firCachesFactory.createCache { callableId, _ ->
generateMemberFunctions(callableId)
}
private val propertyCache: FirCache<Name, List<FirPropertySymbol>, Nothing?> = firCachesFactory.createCache { callableId, _ ->
generateMemberProperties(callableId)
}
private val constructorCache: FirLazyValue<List<FirConstructorSymbol>, Nothing?> = firCachesFactory.createLazyValue {
generateConstructors()
}
private val callableNamesCache: FirLazyValue<Set<Name>, Nothing?> = firCachesFactory.createLazyValue {
extensions.flatMapTo(mutableSetOf()) { it.getCallableNamesForClass(firClass.symbol) }
}
// ------------------------------------------ generators ------------------------------------------
private fun generateMemberFunctions(name: Name): List<FirNamedFunctionSymbol> {
return extensions
.flatMap { it.generateFunctions(CallableId(firClass.classId, name), firClass.symbol) }
.onEach { it.fir.validate() }
}
private fun generateMemberProperties(name: Name): List<FirPropertySymbol> {
return extensions
.flatMap { it.generateProperties(CallableId(firClass.classId, name), firClass.symbol) }
.onEach { it.fir.validate() }
}
private fun generateConstructors(): List<FirConstructorSymbol> {
val classId = firClass.symbol.classId
val callableId = if (classId.isNestedClass) {
CallableId(classId.parentClassId!!, classId.shortClassName)
} else {
CallableId(classId.asSingleFqName().parent(), classId.shortClassName)
}
return extensions.flatMap { it.generateConstructors(callableId) }.onEach { it.fir.validate() }
}
// ------------------------------------------ scope methods ------------------------------------------
override fun getCallableNames(): Set<Name> {
return callableNamesCache.getValue()
}
override fun getClassifierNames(): Set<Name> {
return nestedClassifierScope?.getClassifierNames() ?: emptySet()
}
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
nestedClassifierScope?.processClassifiersByNameWithSubstitution(name, processor)
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
if (name !in getCallableNames()) return
for (functionSymbol in functionCache.getValue(name)) {
processor(functionSymbol)
}
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
if (name !in getCallableNames()) return
for (propertySymbol in propertyCache.getValue(name)) {
processor(propertySymbol)
}
}
override fun processDeclaredConstructors(processor: (FirConstructorSymbol) -> Unit) {
for (constructorSymbol in constructorCache.getValue()) {
processor(constructorSymbol)
}
}
}
class FirGeneratedClassNestedClassifierScope(
klass: FirClass,
useSiteSession: FirSession
) : FirNestedClassifierScope(klass, useSiteSession) {
private val extensions = klass.findGeneratedExtensions(useSiteSession) { needToGenerateNestedClassifiersInClass(it) }
private val nestedClassifierCache: FirCache<Name, FirRegularClassSymbol?, Nothing?> =
useSiteSession.firCachesFactory.createCache { name, _ ->
generateNestedClassifier(name)
}
private val nestedClassifiersNames: FirLazyValue<Set<Name>, Nothing?> =
useSiteSession.firCachesFactory.createLazyValue {
extensions.flatMapTo(mutableSetOf()) { it.getNestedClassifiersNames(klass.symbol) }
}
private fun generateNestedClassifier(name: Name): FirRegularClassSymbol? {
if (name !in getClassifierNames()) return null
val generatedClass = useSiteSession.symbolProvider.getClassLikeSymbolByClassId(klass.classId.createNestedClassId(name))
require(generatedClass is FirRegularClassSymbol?) { "Only regular class are allowed as nested classes" }
return generatedClass
}
override fun getNestedClassSymbol(name: Name): FirRegularClassSymbol? {
return nestedClassifierCache.getValue(name)
}
override fun isEmpty(): Boolean {
return getClassifierNames().isEmpty()
}
override fun getClassifierNames(): Set<Name> {
return nestedClassifiersNames.getValue()
}
}
private inline fun FirClass.findGeneratedExtensions(
useSiteSession: FirSession,
predicate: FirDeclarationGenerationExtension.(FirClass) -> Boolean
): List<FirDeclarationGenerationExtension> {
val origin = origin
val declarationGenerators = useSiteSession.extensionService.declarationGenerators
return if (origin is FirDeclarationOrigin.Plugin) {
declarationGenerators.filter { it.key == origin.key }.also {
require(it.isNotEmpty()) { "Extension for ${origin.key} not found" }
}
} else {
declarationGenerators.filter { it.predicate(this) }
}
}
@@ -0,0 +1,7 @@
/*
* Copyright 2010-2021 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.scopes.impl
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirImplementationDetail
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.builder.FirAnnotationContainerBuilder
import org.jetbrains.kotlin.fir.builder.FirBuilderDsl
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirArgumentList
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirFunctionCallOrigin
import org.jetbrains.kotlin.fir.expressions.builder.FirCallBuilder
import org.jetbrains.kotlin.fir.expressions.builder.FirExpressionBuilder
import org.jetbrains.kotlin.fir.expressions.builder.FirQualifiedAccessBuilder
import org.jetbrains.kotlin.fir.expressions.impl.FirFunctionCallImpl
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildImplicitTypeRef
@OptIn(FirImplementationDetail::class)
class FirIntegerOperatorCall @FirImplementationDetail constructor(
source: FirSourceElement?,
typeRef: FirTypeRef,
annotations: MutableList<FirAnnotation>,
typeArguments: MutableList<FirTypeProjection>,
explicitReceiver: FirExpression?,
dispatchReceiver: FirExpression,
extensionReceiver: FirExpression,
argumentList: FirArgumentList,
calleeReference: FirNamedReference,
) : FirFunctionCallImpl(
source,
typeRef,
annotations,
typeArguments,
explicitReceiver,
dispatchReceiver,
extensionReceiver,
argumentList,
calleeReference,
FirFunctionCallOrigin.Operator
)
@FirBuilderDsl
class FirIntegerOperatorCallBuilder : FirQualifiedAccessBuilder, FirCallBuilder, FirAnnotationContainerBuilder, FirExpressionBuilder {
override var source: FirSourceElement? = null
override var typeRef: FirTypeRef = buildImplicitTypeRef()
override val annotations: MutableList<FirAnnotation> = mutableListOf()
override val typeArguments: MutableList<FirTypeProjection> = mutableListOf()
override var explicitReceiver: FirExpression? = null
override var dispatchReceiver: FirExpression = FirNoReceiverExpression
override var extensionReceiver: FirExpression = FirNoReceiverExpression
lateinit var calleeReference: FirNamedReference
override lateinit var argumentList: FirArgumentList
@OptIn(FirImplementationDetail::class)
override fun build(): FirIntegerOperatorCall {
return FirIntegerOperatorCall(
source,
typeRef,
annotations,
typeArguments,
explicitReceiver,
dispatchReceiver,
extensionReceiver,
argumentList,
calleeReference,
)
}
}
inline fun buildIntegerOperatorFunctionCall(init: FirIntegerOperatorCallBuilder.() -> Unit): FirIntegerOperatorCall {
return FirIntegerOperatorCallBuilder().apply(init).build()
}
@@ -0,0 +1,39 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
// TODO: we could get rid of this scope and use FirNestedClassifierScope instead,
// but in this case we should make JavaSymbolProvider greedy related to nested classifiers
// (or make possible to calculate nested classifiers on-the-fly)
class FirLazyNestedClassifierScope(
val classId: ClassId,
private val existingNames: List<Name>,
private val symbolProvider: FirSymbolProvider
) : FirContainingNamesAwareScope() {
override fun processClassifiersByNameWithSubstitution(
name: Name,
processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit
) {
if (name !in existingNames) {
return
}
val child = classId.createNestedClassId(name)
val symbol = symbolProvider.getClassLikeSymbolByClassId(child) ?: return
processor(symbol, ConeSubstitutor.Empty)
}
override fun getClassifierNames(): Set<Name> = existingNames.toSet()
override fun getCallableNames(): Set<Name> = emptySet()
}
@@ -0,0 +1,85 @@
/*
* Copyright 2010-2021 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.scopes.impl
import kotlinx.collections.immutable.PersistentMap
import kotlinx.collections.immutable.persistentMapOf
import org.jetbrains.kotlin.builtins.StandardNames.BACKING_FIELD
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.FirVariable
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.util.PersistentMultimap
import org.jetbrains.kotlin.name.Name
class FirLocalScope private constructor(
val properties: PersistentMap<Name, FirVariableSymbol<*>>,
val functions: PersistentMultimap<Name, FirNamedFunctionSymbol>,
val classes: PersistentMap<Name, FirRegularClassSymbol>
) : FirContainingNamesAwareScope() {
constructor() : this(persistentMapOf(), PersistentMultimap(), persistentMapOf())
fun storeClass(klass: FirRegularClass): FirLocalScope {
return FirLocalScope(
properties, functions, classes.put(klass.name, klass.symbol)
)
}
fun storeFunction(function: FirSimpleFunction): FirLocalScope {
return FirLocalScope(
properties, functions.put(function.name, function.symbol), classes
)
}
fun storeVariable(variable: FirVariable): FirLocalScope {
return FirLocalScope(
properties.put(variable.name, variable.symbol), functions, classes
)
}
fun storeBackingField(property: FirProperty): FirLocalScope {
val enhancedProperties = property.backingField?.symbol?.let {
properties.put(BACKING_FIELD, it)
}
return FirLocalScope(
enhancedProperties ?: properties,
functions,
classes
)
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
for (function in functions[name]) {
processor(function)
}
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
val property = properties[name]
if (property != null) {
processor(property)
}
}
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
val klass = classes[name]
if (klass != null) {
processor(klass, ConeSubstitutor.Empty)
}
}
override fun mayContainName(name: Name) = properties.containsKey(name) || functions[name].isNotEmpty() || classes.containsKey(name)
override fun getCallableNames(): Set<Name> = properties.keys + functions.keys
override fun getClassifierNames(): Set<Name> = classes.keys
}
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
import org.jetbrains.kotlin.fir.scopes.FirTypeParameterScope
import org.jetbrains.kotlin.name.Name
class FirMemberTypeParameterScope(callableMember: FirMemberDeclaration) : FirTypeParameterScope() {
override val typeParameters: Map<Name, List<FirTypeParameter>> =
callableMember.typeParameters.filterIsInstance<FirTypeParameter>().groupBy { it.name }
}
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRef
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.name.Name
abstract class FirNestedClassifierScope(val klass: FirClass, val useSiteSession: FirSession) : FirContainingNamesAwareScope() {
protected abstract fun getNestedClassSymbol(name: Name): FirRegularClassSymbol?
override fun processClassifiersByNameWithSubstitution(
name: Name,
processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit
) {
val matchedClass = getNestedClassSymbol(name) ?: return
val substitution = klass.typeParameters.associate {
it.symbol to it.toConeType()
}
processor(matchedClass, ConeSubstitutorByMap(substitution, useSiteSession))
}
abstract fun isEmpty(): Boolean
override fun getCallableNames(): Set<Name> = emptySet()
}
class FirNestedClassifierScopeImpl(klass: FirClass, useSiteSession: FirSession) : FirNestedClassifierScope(klass, useSiteSession) {
private val classIndex: Map<Name, FirRegularClassSymbol> = run {
val result = mutableMapOf<Name, FirRegularClassSymbol>()
for (declaration in klass.declarations) {
if (declaration is FirRegularClass) {
result[declaration.name] = declaration.symbol
}
}
result
}
override fun getNestedClassSymbol(name: Name): FirRegularClassSymbol? {
return classIndex[name]
}
override fun isEmpty(): Boolean = classIndex.isEmpty()
override fun getClassifierNames(): Set<Name> = classIndex.keys
}
class FirCompositeNestedClassifierScope(
val scopes: List<FirNestedClassifierScope>,
klass: FirClass,
useSiteSession: FirSession
) : FirNestedClassifierScope(klass, useSiteSession) {
override fun getNestedClassSymbol(name: Name): FirRegularClassSymbol? {
error("Should not be called")
}
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
scopes.forEach { it.processClassifiersByNameWithSubstitution(name, processor) }
}
override fun isEmpty(): Boolean {
return scopes.all { it.isEmpty() }
}
override fun getClassifierNames(): Set<Name> {
return scopes.flatMapTo(mutableSetOf()) { it.getClassifierNames() }
}
}
fun FirTypeParameterRef.toConeType(): ConeKotlinType = symbol.toConeType()
fun FirTypeParameterSymbol.toConeType(): ConeKotlinType = ConeTypeParameterTypeImpl(ConeTypeParameterLookupTag(this), isNullable = false)
@@ -0,0 +1,58 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.resolve.createSubstitutionForSupertype
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.scopes.getSingleClassifier
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.name.Name
private class FirNestedClassifierScopeWithSubstitution(
private val scope: FirContainingNamesAwareScope,
private val substitutor: ConeSubstitutor
) : FirContainingNamesAwareScope() {
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
scope.processFunctionsByName(name, processor)
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
scope.processPropertiesByName(name, processor)
}
override fun processDeclaredConstructors(processor: (FirConstructorSymbol) -> Unit) {
scope.processDeclaredConstructors(processor)
}
override fun mayContainName(name: Name): Boolean {
return scope.mayContainName(name)
}
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
val matchedClass = scope.getSingleClassifier(name) as? FirRegularClassSymbol ?: return
val substitutor = substitutor.takeIf { matchedClass.fir.isInner } ?: ConeSubstitutor.Empty
processor(matchedClass, substitutor)
}
override fun getCallableNames(): Set<Name> = scope.getCallableNames()
override fun getClassifierNames(): Set<Name> = scope.getClassifierNames()
override val scopeOwnerLookupNames: List<String>
get() = scope.scopeOwnerLookupNames
}
fun FirContainingNamesAwareScope.wrapNestedClassifierScopeWithSubstitutionForSuperType(
superType: ConeClassLikeType,
session: FirSession
): FirContainingNamesAwareScope {
val substitutor = createSubstitutionForSupertype(superType, session)
return FirNestedClassifierScopeWithSubstitution(this, substitutor)
}
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataKey
import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataRegistry
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.builder.buildPropertyCopy
import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunctionCopy
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
class FirObjectImportedCallableScope(
private val importedClassId: ClassId,
private val objectUseSiteScope: FirTypeScope
) : FirContainingNamesAwareScope() {
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
objectUseSiteScope.processFunctionsByName(name) wrapper@{ symbol ->
val function = symbol.fir
val syntheticFunction = buildSimpleFunctionCopy(function) {
origin = FirDeclarationOrigin.ImportedFromObject
this.symbol = FirNamedFunctionSymbol(CallableId(importedClassId, name))
}.apply {
importedFromObjectData = ImportedFromObjectData(importedClassId, function)
}
processor(syntheticFunction.symbol)
}
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
objectUseSiteScope.processPropertiesByName(name) wrapper@{ symbol ->
if (symbol !is FirPropertySymbol) {
processor(symbol)
return@wrapper
}
val property = symbol.fir
val syntheticFunction = buildPropertyCopy(property) {
origin = FirDeclarationOrigin.ImportedFromObject
this.symbol = FirPropertySymbol(CallableId(importedClassId, name))
this.delegateFieldSymbol = null
}.apply {
importedFromObjectData = ImportedFromObjectData(importedClassId, property)
}
processor(syntheticFunction.symbol)
}
}
override fun getCallableNames(): Set<Name> = objectUseSiteScope.getCallableNames()
override fun getClassifierNames(): Set<Name> = emptySet()
}
private object ImportedFromObjectClassIdKey : FirDeclarationDataKey()
class ImportedFromObjectData<D : FirCallableDeclaration>(
val objectClassId: ClassId,
val original: D,
)
var <D : FirCallableDeclaration>
D.importedFromObjectData: ImportedFromObjectData<D>? by FirDeclarationDataRegistry.data(ImportedFromObjectClassIdKey)
@@ -0,0 +1,42 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.name.Name
class FirOnlyCallablesScope(val delegate: FirScope) : FirScope() {
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
return delegate.processFunctionsByName(name, processor)
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
return delegate.processPropertiesByName(name, processor)
}
override val scopeOwnerLookupNames: List<String>
get() = delegate.scopeOwnerLookupNames
}
class FirNameAwareOnlyCallablesScope(val delegate: FirContainingNamesAwareScope) : FirContainingNamesAwareScope() {
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
return delegate.processFunctionsByName(name, processor)
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
return delegate.processPropertiesByName(name, processor)
}
override val scopeOwnerLookupNames: List<String>
get() = delegate.scopeOwnerLookupNames
override fun getCallableNames(): Set<Name> = delegate.getCallableNames()
override fun getClassifierNames(): Set<Name> = emptySet()
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
import org.jetbrains.kotlin.name.Name
class FirOnlyClassifiersScope(val delegate: FirScope) : FirScope() {
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
return delegate.processClassifiersByNameWithSubstitution(name, processor)
}
}
class FirNameAwareOnlyClassifiersScope(val delegate: FirContainingNamesAwareScope) : FirContainingNamesAwareScope() {
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
return delegate.processClassifiersByNameWithSubstitution(name, processor)
}
override fun getCallableNames(): Set<Name> = emptySet()
override fun getClassifierNames(): Set<Name> = delegate.getClassifierNames()
}
@@ -0,0 +1,69 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.scopeSessionKey
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.SmartList
class FirPackageMemberScope(
val fqName: FqName,
val session: FirSession,
private val symbolProvider: FirSymbolProvider = session.symbolProvider
) : FirScope() {
private val classifierCache: MutableMap<Name, FirClassifierSymbol<*>?> = mutableMapOf()
private val functionCache: MutableMap<Name, List<FirNamedFunctionSymbol>> = mutableMapOf()
private val propertyCache: MutableMap<Name, List<FirPropertySymbol>> = mutableMapOf()
override fun processClassifiersByNameWithSubstitution(
name: Name,
processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit
) {
if (name.asString().isEmpty()) return
val symbol = classifierCache.getOrPut(name) {
val unambiguousFqName = ClassId(fqName, name)
symbolProvider.getClassLikeSymbolByClassId(unambiguousFqName)
}
if (symbol != null) {
processor(symbol, ConeSubstitutor.Empty)
}
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
val symbols = functionCache.getOrPut(name) {
symbolProvider.getTopLevelFunctionSymbols(fqName, name)
}
for (symbol in symbols) {
processor(symbol)
}
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
val symbols = propertyCache.getOrPut(name) {
symbolProvider.getTopLevelPropertySymbols(fqName, name)
}
for (symbol in symbols) {
processor(symbol)
}
}
override val scopeOwnerLookupNames: List<String> = SmartList(fqName.asString())
}
val PACKAGE_MEMBER = scopeSessionKey<FqName, FirPackageMemberScope>()
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirTypedDeclaration
import org.jetbrains.kotlin.fir.isIntersectionOverride
import org.jetbrains.kotlin.fir.isSubstitutionOverride
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.name.Name
class FirScopeWithFakeOverrideTypeCalculator(
private val delegate: FirTypeScope,
private val fakeOverrideTypeCalculator: FakeOverrideTypeCalculator
) : FirTypeScope() {
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
delegate.processClassifiersByNameWithSubstitution(name, processor)
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
delegate.processFunctionsByName(name) {
updateReturnType(it.fir)
processor(it)
}
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
delegate.processPropertiesByName(name) {
updateReturnType(it.fir)
processor(it)
}
}
override fun processDeclaredConstructors(processor: (FirConstructorSymbol) -> Unit) {
delegate.processDeclaredConstructors(processor)
}
override fun mayContainName(name: Name): Boolean {
return delegate.mayContainName(name)
}
override fun processDirectOverriddenFunctionsWithBaseScope(
functionSymbol: FirNamedFunctionSymbol,
processor: (FirNamedFunctionSymbol, FirTypeScope) -> ProcessorAction
): ProcessorAction {
return delegate.processDirectOverriddenFunctionsWithBaseScope(functionSymbol) { symbol, scope ->
updateReturnType(symbol.fir)
processor(symbol, scope)
}
}
override fun processDirectOverriddenPropertiesWithBaseScope(
propertySymbol: FirPropertySymbol,
processor: (FirPropertySymbol, FirTypeScope) -> ProcessorAction
): ProcessorAction {
return delegate.processDirectOverriddenPropertiesWithBaseScope(propertySymbol) { symbol, scope ->
updateReturnType(symbol.fir)
processor(symbol, scope)
}
}
override fun getCallableNames(): Set<Name> {
return delegate.getCallableNames()
}
override fun getClassifierNames(): Set<Name> {
return delegate.getClassifierNames()
}
private fun updateReturnType(declaration: FirTypedDeclaration) {
if (declaration !is FirCallableDeclaration) return
if (declaration.isSubstitutionOverride || declaration.isIntersectionOverride) {
fakeOverrideTypeCalculator.computeReturnType(declaration)
}
}
override val scopeOwnerLookupNames: List<String>
get() = delegate.scopeOwnerLookupNames
}
@@ -0,0 +1,152 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolvedTypeDeclaration
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
class FirStandardOverrideChecker(private val session: FirSession) : FirAbstractOverrideChecker() {
private val context = session.typeContext
private fun isEqualTypes(substitutedCandidateType: ConeKotlinType, substitutedBaseType: ConeKotlinType): Boolean {
return with(context) {
val baseIsFlexible = substitutedBaseType.isFlexible()
val candidateIsFlexible = substitutedCandidateType.isFlexible()
if (baseIsFlexible == candidateIsFlexible) {
return AbstractTypeChecker.equalTypes(context, substitutedCandidateType, substitutedBaseType)
}
val lowerBound: SimpleTypeMarker
val upperBound: SimpleTypeMarker
val type: KotlinTypeMarker
if (baseIsFlexible) {
lowerBound = substitutedBaseType.lowerBoundIfFlexible()
upperBound = substitutedBaseType.upperBoundIfFlexible()
type = substitutedCandidateType
} else {
lowerBound = substitutedCandidateType.lowerBoundIfFlexible()
upperBound = substitutedCandidateType.upperBoundIfFlexible()
type = substitutedBaseType
}
AbstractTypeChecker.isSubtypeOf(context, lowerBound, type) && AbstractTypeChecker.isSubtypeOf(context, type, upperBound)
}
}
private fun isEqualTypes(candidateType: ConeKotlinType, baseType: ConeKotlinType, substitutor: ConeSubstitutor): Boolean {
val substitutedCandidateType = substitutor.substituteOrSelf(candidateType)
val substitutedBaseType = substitutor.substituteOrSelf(baseType)
return isEqualTypes(substitutedCandidateType, substitutedBaseType)
}
fun isEqualTypes(candidateTypeRef: FirTypeRef, baseTypeRef: FirTypeRef, substitutor: ConeSubstitutor): Boolean {
candidateTypeRef.ensureResolvedTypeDeclaration(session, requiredPhase = FirResolvePhase.TYPES)
baseTypeRef.ensureResolvedTypeDeclaration(session, requiredPhase = FirResolvePhase.TYPES)
if (candidateTypeRef is FirErrorTypeRef && baseTypeRef is FirErrorTypeRef) {
return maybeEqualErrorTypes(candidateTypeRef, baseTypeRef)
}
return isEqualTypes(candidateTypeRef.coneType, baseTypeRef.coneType, substitutor)
}
private fun maybeEqualErrorTypes(ref1: FirErrorTypeRef, ref2: FirErrorTypeRef): Boolean {
val delegated1 = ref1.delegatedTypeRef as? FirUserTypeRef ?: return false
val delegated2 = ref2.delegatedTypeRef as? FirUserTypeRef ?: return false
if (delegated1.qualifier.size != delegated2.qualifier.size) return false
return delegated1.qualifier.zip(delegated2.qualifier).all { (l, r) -> l.name == r.name }
}
/**
* Good case complexity is O(1)
* Worst case complexity is O(N), where N is number of type-parameter bound's
*/
private fun isEqualBound(
overrideBound: FirTypeRef,
baseBound: FirTypeRef,
overrideTypeParameter: FirTypeParameter,
baseTypeParameter: FirTypeParameter,
substitutor: ConeSubstitutor
): Boolean {
val substitutedOverrideType = substitutor.substituteOrSelf(overrideBound.coneType)
val substitutedBaseType = substitutor.substituteOrSelf(baseBound.coneType)
if (isEqualTypes(substitutedOverrideType, substitutedBaseType)) return true
return overrideTypeParameter.bounds.any { bound -> isEqualTypes(bound.coneType, substitutedBaseType, substitutor) } &&
baseTypeParameter.bounds.any { bound -> isEqualTypes(bound.coneType, substitutedOverrideType, substitutor) }
}
private fun isCompatibleTypeParameters(
overrideCandidate: FirTypeParameterRef,
baseDeclaration: FirTypeParameterRef,
substitutor: ConeSubstitutor
): Boolean {
if (overrideCandidate.symbol == baseDeclaration.symbol) return true
if (overrideCandidate !is FirTypeParameter || baseDeclaration !is FirTypeParameter) return false
if (overrideCandidate.bounds.size != baseDeclaration.bounds.size) return false
return overrideCandidate.bounds.zip(baseDeclaration.bounds)
.all { (aBound, bBound) -> isEqualBound(aBound, bBound, overrideCandidate, baseDeclaration, substitutor) }
}
override fun buildTypeParametersSubstitutorIfCompatible(
overrideCandidate: FirCallableDeclaration,
baseDeclaration: FirCallableDeclaration
): ConeSubstitutor? {
overrideCandidate.ensureResolved(FirResolvePhase.TYPES)
baseDeclaration.ensureResolved(FirResolvePhase.TYPES)
val substitutor = buildSubstitutorForOverridesCheck(overrideCandidate, baseDeclaration, session) ?: return null
if (
overrideCandidate.typeParameters.isNotEmpty() &&
overrideCandidate.typeParameters.zip(baseDeclaration.typeParameters).any { (override, base) ->
!isCompatibleTypeParameters(override, base, substitutor)
}
) return null
return substitutor
}
private fun isEqualReceiverTypes(candidateTypeRef: FirTypeRef?, baseTypeRef: FirTypeRef?, substitutor: ConeSubstitutor): Boolean {
return when {
candidateTypeRef != null && baseTypeRef != null -> isEqualTypes(candidateTypeRef, baseTypeRef, substitutor)
else -> candidateTypeRef == null && baseTypeRef == null
}
}
override fun isOverriddenFunction(overrideCandidate: FirSimpleFunction, baseDeclaration: FirSimpleFunction): Boolean {
if (Visibilities.isPrivate(baseDeclaration.visibility)) return false
if (overrideCandidate.valueParameters.size != baseDeclaration.valueParameters.size) return false
val substitutor = buildTypeParametersSubstitutorIfCompatible(overrideCandidate, baseDeclaration) ?: return false
overrideCandidate.ensureResolved(FirResolvePhase.TYPES)
baseDeclaration.ensureResolved(FirResolvePhase.TYPES)
if (!isEqualReceiverTypes(overrideCandidate.receiverTypeRef, baseDeclaration.receiverTypeRef, substitutor)) return false
return overrideCandidate.valueParameters.zip(baseDeclaration.valueParameters).all { (memberParam, selfParam) ->
isEqualTypes(memberParam.returnTypeRef, selfParam.returnTypeRef, substitutor)
}
}
override fun isOverriddenProperty(
overrideCandidate: FirCallableDeclaration,
baseDeclaration: FirProperty
): Boolean {
if (Visibilities.isPrivate(baseDeclaration.visibility)) return false
if (overrideCandidate !is FirProperty) return false
val substitutor = buildTypeParametersSubstitutorIfCompatible(overrideCandidate, baseDeclaration) ?: return false
overrideCandidate.ensureResolved(FirResolvePhase.TYPES)
baseDeclaration.ensureResolved(FirResolvePhase.TYPES)
return isEqualReceiverTypes(overrideCandidate.receiverTypeRef, baseDeclaration.receiverTypeRef, substitutor)
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.name.Name
class FirStaticScope(private val delegateScope: FirContainingNamesAwareScope) : FirContainingNamesAwareScope() {
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
delegateScope.processClassifiersByNameWithSubstitution(name, processor)
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
delegateScope.processFunctionsByName(name) {
if ((it.fir as? FirSimpleFunction)?.isStatic == true) {
processor(it)
}
}
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
delegateScope.processPropertiesByName(name) {
if ((it.fir as? FirCallableDeclaration)?.isStatic == true) {
processor(it)
}
}
}
override fun getCallableNames(): Set<Name> {
return delegateScope.getCallableNames()
}
override fun getClassifierNames(): Set<Name> {
return delegateScope.getClassifierNames()
}
}
@@ -0,0 +1,617 @@
/*
* Copyright 2010-2021 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.scopes.impl
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.caches.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.AbstractTypeChecker
class FirTypeIntersectionScope private constructor(
session: FirSession,
overrideChecker: FirOverrideChecker,
private val scopes: List<FirTypeScope>,
private val dispatchReceiverType: ConeKotlinType,
) : AbstractFirOverrideScope(session, overrideChecker) {
private val absentFunctions: MutableSet<Name> = mutableSetOf()
private val absentProperties: MutableSet<Name> = mutableSetOf()
private val absentClassifiers: MutableSet<Name> = mutableSetOf()
private val typeCheckerState = session.typeContext.newTypeCheckerState(
errorTypesEqualToAnything = false,
stubTypesEqualToAnything = false
)
private val overriddenSymbols: MutableMap<FirCallableSymbol<*>, Collection<MemberWithBaseScope<FirCallableSymbol<*>>>> = mutableMapOf()
private val intersectionOverrides =
session.intersectionOverrideStorage.cacheByScope.getValue(dispatchReceiverType).intersectionOverrides
private val callableNamesCached by lazy(LazyThreadSafetyMode.PUBLICATION) {
scopes.flatMapTo(mutableSetOf()) { it.getCallableNames() }
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
if (!processCallablesByName(name, processor, absentFunctions, FirScope::processFunctionsByName)) {
super.processFunctionsByName(name, processor)
}
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
if (!processCallablesByName(name, processor, absentProperties, FirScope::processPropertiesByName)) {
super.processPropertiesByName(name, processor)
}
}
private inline fun <D : FirCallableSymbol<*>> processCallablesByName(
name: Name,
noinline processor: (D) -> Unit,
absentNames: MutableSet<Name>,
processCallables: FirScope.(Name, (D) -> Unit) -> Unit
): Boolean {
if (name in absentNames) {
return false
}
val membersByScope = scopes.mapNotNull { scope ->
val resultForScope = mutableListOf<D>()
scope.processCallables(name) {
if (it !is FirConstructorSymbol) {
resultForScope.add(it)
}
}
resultForScope.takeIf { it.isNotEmpty() }?.let {
scope to it
}
}
if (membersByScope.isEmpty()) {
absentNames.add(name)
return false
}
membersByScope.singleOrNull()?.let { (scope, members) ->
for (member in members) {
overriddenSymbols[member] = listOf(MemberWithBaseScope(member, scope))
processor(member)
}
return false
}
val allMembersWithScope = membersByScope.flatMapTo(linkedSetOf()) { (scope, members) ->
members.map { MemberWithBaseScope(it, scope) }
}
while (allMembersWithScope.size > 1) {
val maxByVisibility = findMemberWithMaxVisibility(allMembersWithScope)
val extractBothWaysWithPrivate = extractBothWaysOverridable(maxByVisibility, allMembersWithScope)
val extractedOverrides = extractBothWaysWithPrivate.filterNotTo(mutableListOf()) {
Visibilities.isPrivate((it.member.fir as FirMemberDeclaration).visibility)
}.takeIf { it.isNotEmpty() } ?: extractBothWaysWithPrivate
val baseMembersForIntersection = extractedOverrides.calcBaseMembersForIntersectionOverride()
if (baseMembersForIntersection.size > 1) {
val (mostSpecific, scopeForMostSpecific) = selectMostSpecificMember(baseMembersForIntersection)
val intersectionOverride = intersectionOverrides.getValue(
mostSpecific,
FirIntersectionOverrideStorage.ContextForIntersectionOverrideConstruction(
this,
extractedOverrides,
scopeForMostSpecific
)
)
overriddenSymbols[intersectionOverride.member] = extractedOverrides
@Suppress("UNCHECKED_CAST")
processor(intersectionOverride.member as D)
} else {
val mostSpecific = baseMembersForIntersection.single().member
overriddenSymbols[mostSpecific] = extractedOverrides
processor(mostSpecific)
}
}
if (allMembersWithScope.isNotEmpty()) {
val single = allMembersWithScope.single().member
overriddenSymbols[single] = allMembersWithScope.toList()
processor(single)
}
return true
}
private inline fun <reified D : FirCallableDeclaration> D.unwrapSubstitutionOverrides(): D {
var current = this
do {
val next = current.originalForSubstitutionOverride ?: return current
current = next
} while (true)
}
fun <D : FirCallableSymbol<*>> createIntersectionOverride(
extractedOverrides: List<MemberWithBaseScope<D>>,
mostSpecific: D,
scopeForMostSpecific: FirTypeScope
): MemberWithBaseScope<FirCallableSymbol<*>> {
val newModality = chooseIntersectionOverrideModality(extractedOverrides)
val newVisibility = chooseIntersectionVisibility(extractedOverrides)
val extractedOverridesSymbols = extractedOverrides.map { it.member }
return when (mostSpecific) {
is FirNamedFunctionSymbol -> createIntersectionOverride(mostSpecific, extractedOverridesSymbols, newModality, newVisibility)
is FirPropertySymbol -> createIntersectionOverride(mostSpecific, extractedOverridesSymbols, newModality, newVisibility)
else -> throw IllegalStateException("Should not be here")
}.withScope(scopeForMostSpecific)
}
private fun <S : FirCallableSymbol<*>>
MutableList<MemberWithBaseScope<S>>.calcBaseMembersForIntersectionOverride(): List<MemberWithBaseScope<S>> {
if (size == 1) return this
val unwrappedMemberSet = mutableSetOf<MemberWithBaseScope<S>>()
for ((member, scope) in this) {
@Suppress("UNCHECKED_CAST")
unwrappedMemberSet += MemberWithBaseScope(member.fir.unwrapSubstitutionOverrides().symbol as S, scope)
}
// If in fact extracted overrides are the same symbols,
// we should just take most specific member without creating intersection
// A typical sample here is inheritance of the same class in different places of hierarchy
if (unwrappedMemberSet.size == 1) {
return listOf(selectMostSpecificMember(this))
}
val baseMembers = mutableSetOf<S>()
for ((member, scope) in this) {
@Suppress("UNCHECKED_CAST")
if (member is FirNamedFunctionSymbol) {
scope.processOverriddenFunctions(member) {
val symbol = it.fir.unwrapSubstitutionOverrides().symbol
if (symbol != member.fir.unwrapSubstitutionOverrides().symbol) {
baseMembers += symbol as S
}
ProcessorAction.NEXT
}
} else if (member is FirPropertySymbol) {
scope.processOverriddenProperties(member) {
val symbol = it.fir.unwrapSubstitutionOverrides().symbol
if (symbol != member.fir.unwrapSubstitutionOverrides().symbol) {
baseMembers += symbol as S
}
ProcessorAction.NEXT
}
}
}
removeIf { (member, _) -> member.fir.unwrapSubstitutionOverrides().symbol in baseMembers }
return this
}
private fun <D : FirCallableSymbol<*>> chooseIntersectionOverrideModality(
extractedOverridden: Collection<MemberWithBaseScope<D>>
): Modality? {
var hasOpen = false
var hasAbstract = false
for ((member) in extractedOverridden) {
when ((member.fir as FirMemberDeclaration).modality) {
Modality.FINAL -> return Modality.FINAL
Modality.SEALED -> {
// Members should not be sealed. But, that will be reported as WRONG_MODIFIER_TARGET, and here we shouldn't raise an
// internal error. Instead, let the intersection override have the default modality: null.
return null
}
Modality.OPEN -> {
hasOpen = true
}
Modality.ABSTRACT -> {
hasAbstract = true
}
null -> {
}
}
}
if (hasAbstract && !hasOpen) return Modality.ABSTRACT
if (!hasAbstract && hasOpen) return Modality.OPEN
@Suppress("UNCHECKED_CAST")
val processDirectOverridden: ProcessOverriddenWithBaseScope<D> = when (extractedOverridden.first().member) {
is FirNamedFunctionSymbol -> FirTypeScope::processDirectOverriddenFunctionsWithBaseScope as ProcessOverriddenWithBaseScope<D>
is FirPropertySymbol -> FirTypeScope::processDirectOverriddenPropertiesWithBaseScope as ProcessOverriddenWithBaseScope<D>
else -> error("Unexpected callable kind: ${extractedOverridden.first().member}")
}
val realOverridden = extractedOverridden.flatMap { realOverridden(it.member, it.baseScope, processDirectOverridden) }
val filteredOverridden = filterOutOverridden(realOverridden, processDirectOverridden)
return filteredOverridden.minOf { (it.member.fir as FirMemberDeclaration).modality ?: Modality.ABSTRACT }
}
private fun <D : FirCallableSymbol<*>> realOverridden(
symbol: D,
scope: FirTypeScope,
processDirectOverridden: ProcessOverriddenWithBaseScope<D>,
): Collection<MemberWithBaseScope<D>> {
val result = mutableSetOf<MemberWithBaseScope<D>>()
collectRealOverridden(symbol, scope, result, mutableSetOf(), processDirectOverridden)
return result
}
private fun <D : FirCallableSymbol<*>> collectRealOverridden(
symbol: D,
scope: FirTypeScope,
result: MutableCollection<MemberWithBaseScope<D>>,
visited: MutableSet<D>,
processDirectOverridden: FirTypeScope.(D, (D, FirTypeScope) -> ProcessorAction) -> ProcessorAction,
) {
if (!visited.add(symbol)) return
if (!symbol.fir.origin.fromSupertypes) {
result.add(MemberWithBaseScope(symbol, scope))
return
}
scope.processDirectOverridden(symbol) { overridden, baseScope ->
collectRealOverridden(overridden, baseScope, result, visited, processDirectOverridden)
ProcessorAction.NEXT
}
}
private fun <D : FirCallableSymbol<*>> filterOutOverridden(
extractedOverridden: Collection<MemberWithBaseScope<D>>,
processAllOverridden: ProcessOverriddenWithBaseScope<D>,
): Collection<MemberWithBaseScope<D>> {
return extractedOverridden.filter { overridden1 ->
extractedOverridden.none { overridden2 ->
overridden1 !== overridden2 && overrides(
overridden2,
overridden1,
processAllOverridden
)
}
}
}
// Whether f overrides g
private fun <D : FirCallableSymbol<*>> overrides(
f: MemberWithBaseScope<D>,
g: MemberWithBaseScope<D>,
processAllOverridden: ProcessOverriddenWithBaseScope<D>,
): Boolean {
val (fMember, fScope) = f
val (gMember) = g
var result = false
fScope.processAllOverridden(fMember) { overridden, _ ->
if (overridden == gMember) {
result = true
ProcessorAction.STOP
} else {
ProcessorAction.NEXT
}
}
return result
}
private fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
extractedOverrides: Collection<MemberWithBaseScope<D>>
): Visibility {
var maxVisibility: Visibility = Visibilities.Private
for ((override) in extractedOverrides) {
val visibility = (override.fir as FirMemberDeclaration).visibility
// TODO: There is more complex logic at org.jetbrains.kotlin.resolve.OverridingUtil.resolveUnknownVisibilityForMember
// TODO: and org.jetbrains.kotlin.resolve.OverridingUtil.findMaxVisibility
val compare = Visibilities.compare(visibility, maxVisibility) ?: return Visibilities.DEFAULT_VISIBILITY
if (compare > 0) {
maxVisibility = visibility
}
}
return maxVisibility
}
private fun createIntersectionOverride(
mostSpecific: FirNamedFunctionSymbol,
overrides: Collection<FirCallableSymbol<*>>,
newModality: Modality?,
newVisibility: Visibility,
): FirNamedFunctionSymbol {
val newSymbol =
FirIntersectionOverrideFunctionSymbol(
CallableId(
dispatchReceiverType.classId ?: mostSpecific.dispatchReceiverClassOrNull()?.classId!!,
mostSpecific.fir.name
),
overrides
)
val mostSpecificFunction = mostSpecific.fir
FirFakeOverrideGenerator.createCopyForFirFunction(
newSymbol,
mostSpecificFunction, session, FirDeclarationOrigin.IntersectionOverride,
mostSpecificFunction.isExpect,
newDispatchReceiverType = dispatchReceiverType,
newModality = newModality,
newVisibility = newVisibility,
).apply {
originalForIntersectionOverrideAttr = mostSpecific.fir
}
return newSymbol
}
private fun createIntersectionOverride(
mostSpecific: FirPropertySymbol,
overrides: Collection<FirCallableSymbol<*>>,
newModality: Modality?,
newVisibility: Visibility,
): FirPropertySymbol {
val callableId = CallableId(
dispatchReceiverType.classId ?: mostSpecific.dispatchReceiverClassOrNull()?.classId!!,
mostSpecific.fir.name
)
val newSymbol = FirIntersectionOverridePropertySymbol(callableId, overrides)
val mostSpecificProperty = mostSpecific.fir
FirFakeOverrideGenerator.createCopyForFirProperty(
newSymbol, mostSpecificProperty, session, FirDeclarationOrigin.IntersectionOverride,
newModality = newModality,
newVisibility = newVisibility,
newDispatchReceiverType = dispatchReceiverType,
).apply {
originalForIntersectionOverrideAttr = mostSpecific.fir
}
return newSymbol
}
private fun <D : FirCallableSymbol<*>> selectMostSpecificMember(overridables: Collection<MemberWithBaseScope<D>>): MemberWithBaseScope<D> {
require(overridables.isNotEmpty()) { "Should have at least one overridable symbol" }
if (overridables.size == 1) {
return overridables.first()
}
val candidates: MutableCollection<MemberWithBaseScope<D>> = ArrayList(2)
var transitivelyMostSpecific: MemberWithBaseScope<D> = overridables.first()
for (candidate in overridables) {
if (overridables.all { isMoreSpecific(candidate.member, it.member) }) {
candidates.add(candidate)
}
if (isMoreSpecific(candidate.member, transitivelyMostSpecific.member) &&
!isMoreSpecific(transitivelyMostSpecific.member, candidate.member)
) {
transitivelyMostSpecific = candidate
}
}
return when {
candidates.isEmpty() -> transitivelyMostSpecific
candidates.size == 1 -> candidates.first()
else -> {
candidates.firstOrNull {
val type = it.member.fir.returnTypeRef.coneTypeSafe<ConeKotlinType>()
type != null && type !is ConeFlexibleType
}?.let { return it }
candidates.first()
}
}
}
private fun isMoreSpecific(
a: FirCallableSymbol<*>,
b: FirCallableSymbol<*>
): Boolean {
val aFir = a.fir
val bFir = b.fir
val substitutor = buildSubstitutorForOverridesCheck(aFir, bFir, session) ?: return false
// NB: these lines throw CCE in modularized tests when changed to just .coneType (FirImplicitTypeRef)
val aReturnType = a.fir.returnTypeRef.coneTypeSafe<ConeKotlinType>()?.let(substitutor::substituteOrSelf) ?: return false
val bReturnType = b.fir.returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: return false
if (aFir is FirSimpleFunction) {
require(bFir is FirSimpleFunction) { "b is " + b.javaClass }
return isTypeMoreSpecific(aReturnType, bReturnType)
}
if (aFir is FirProperty) {
require(bFir is FirProperty) { "b is " + b.javaClass }
// TODO: if (!OverridingUtil.isAccessorMoreSpecific(pa.getSetter(), pb.getSetter())) return false
return if (aFir.isVar && bFir.isVar) {
AbstractTypeChecker.equalTypes(typeCheckerState, aReturnType, bReturnType)
} else { // both vals or var vs val: val can't be more specific then var
!(!aFir.isVar && bFir.isVar) && isTypeMoreSpecific(aReturnType, bReturnType)
}
}
throw IllegalArgumentException("Unexpected callable: " + a.javaClass)
}
private fun isTypeMoreSpecific(a: ConeKotlinType, b: ConeKotlinType): Boolean =
AbstractTypeChecker.isSubtypeOf(typeCheckerState, a, b)
private fun <D : FirCallableSymbol<*>> findMemberWithMaxVisibility(members: Collection<MemberWithBaseScope<D>>): MemberWithBaseScope<D> {
assert(members.isNotEmpty())
var member: MemberWithBaseScope<D>? = null
for (candidate in members) {
if (member == null) {
member = candidate
continue
}
val result = Visibilities.compare(
member.member.fir.status.visibility,
candidate.member.fir.status.visibility
)
if (result != null && result < 0) {
member = candidate
}
}
return member!!
}
private fun <D : FirCallableSymbol<*>> extractBothWaysOverridable(
overrider: MemberWithBaseScope<D>,
members: MutableCollection<MemberWithBaseScope<D>>
): MutableList<MemberWithBaseScope<D>> {
val result = mutableListOf<MemberWithBaseScope<D>>().apply { add(overrider) }
val iterator = members.iterator()
val overrideCandidate = overrider.member.fir
while (iterator.hasNext()) {
val next = iterator.next()
if (next == overrider) {
iterator.remove()
continue
}
if (similarFunctionsOrBothProperties(overrideCandidate, next.member.fir)) {
result.add(next)
iterator.remove()
}
}
return result
}
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
if (name in absentClassifiers) {
return
}
val accepted = HashSet<FirClassifierSymbol<*>>()
val pending = mutableListOf<FirClassifierSymbol<*>>()
var empty = true
for (scope in scopes) {
scope.processClassifiersByNameWithSubstitution(name) { symbol, substitution ->
empty = false
if (symbol !in accepted) {
pending += symbol
processor(symbol, substitution)
}
}
accepted += pending
pending.clear()
}
if (empty) {
absentClassifiers += name
}
super.processClassifiersByNameWithSubstitution(name, processor)
}
@Suppress("UNCHECKED_CAST")
private fun <S : FirCallableSymbol<*>> getDirectOverriddenSymbols(symbol: S): Collection<MemberWithBaseScope<S>> {
val intersectionOverride = intersectionOverrides.getValueIfComputed(symbol)
val allDirectOverridden = overriddenSymbols[symbol].orEmpty() + intersectionOverride?.let {
overriddenSymbols[it.member]
}.orEmpty()
return allDirectOverridden as Collection<MemberWithBaseScope<S>>
}
override fun processDirectOverriddenFunctionsWithBaseScope(
functionSymbol: FirNamedFunctionSymbol,
processor: (FirNamedFunctionSymbol, FirTypeScope) -> ProcessorAction
): ProcessorAction =
processDirectOverriddenCallablesWithBaseScope(
functionSymbol, processor,
FirTypeScope::processDirectOverriddenFunctionsWithBaseScope
)
override fun processDirectOverriddenPropertiesWithBaseScope(
propertySymbol: FirPropertySymbol,
processor: (FirPropertySymbol, FirTypeScope) -> ProcessorAction
): ProcessorAction =
processDirectOverriddenCallablesWithBaseScope(
propertySymbol, processor,
FirTypeScope::processDirectOverriddenPropertiesWithBaseScope
)
private fun <D : FirCallableSymbol<*>> processDirectOverriddenCallablesWithBaseScope(
callableSymbol: D,
processor: (D, FirTypeScope) -> ProcessorAction,
processDirectOverriddenInBaseScope: FirTypeScope.(D, ((D, FirTypeScope) -> ProcessorAction)) -> ProcessorAction
): ProcessorAction {
for ((overridden, baseScope) in getDirectOverriddenSymbols(callableSymbol)) {
if (overridden === callableSymbol) {
if (!baseScope.processDirectOverriddenInBaseScope(callableSymbol, processor)) return ProcessorAction.STOP
} else {
if (!processor(overridden, baseScope)) return ProcessorAction.STOP
}
}
return ProcessorAction.NEXT
}
override fun getCallableNames(): Set<Name> = callableNamesCached
override fun getClassifierNames(): Set<Name> {
return scopes.flatMapTo(hashSetOf()) { it.getClassifierNames() }
}
companion object {
fun prepareIntersectionScope(
session: FirSession,
overrideChecker: FirOverrideChecker,
scopes: List<FirTypeScope>,
dispatchReceiverType: ConeKotlinType,
): FirTypeScope {
scopes.singleOrNull()?.let { return it }
if (scopes.isEmpty()) {
return Empty
}
return FirTypeIntersectionScope(session, overrideChecker, scopes, dispatchReceiverType)
}
}
}
class MemberWithBaseScope<out D : FirCallableSymbol<*>>(val member: D, val baseScope: FirTypeScope) {
operator fun component1() = member
operator fun component2() = baseScope
override fun equals(other: Any?): Boolean {
return other is MemberWithBaseScope<*> && member == other.member
}
override fun hashCode(): Int {
return member.hashCode()
}
}
private fun <D : FirCallableSymbol<*>> D.withScope(baseScope: FirTypeScope) = MemberWithBaseScope(this, baseScope)
class FirIntersectionOverrideStorage(val session: FirSession) : FirSessionComponent {
private val cachesFactory = session.firCachesFactory
class CacheForScope(cachesFactory: FirCachesFactory) {
val intersectionOverrides: FirCache<FirCallableSymbol<*>, MemberWithBaseScope<FirCallableSymbol<*>>, ContextForIntersectionOverrideConstruction<*>> =
cachesFactory.createCache { mostSpecific, context ->
val (intersectionScope, extractedOverrides, scopeForMostSpecific) = context
intersectionScope.createIntersectionOverride(extractedOverrides, mostSpecific, scopeForMostSpecific)
}
}
data class ContextForIntersectionOverrideConstruction<D : FirCallableSymbol<*>>(
val intersectionScope: FirTypeIntersectionScope,
val extractedOverrides: List<MemberWithBaseScope<D>>,
val scopeForMostSpecific: FirTypeScope
)
val cacheByScope: FirCache<ConeKotlinType, CacheForScope, Nothing?> =
cachesFactory.createCache { _ -> CacheForScope(cachesFactory) }
}
private val FirSession.intersectionOverrideStorage: FirIntersectionOverrideStorage by FirSession.sessionComponentAccessor()
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2021 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.name.StandardClassIds
val ConeKotlinType.isArrayOrPrimitiveArray: Boolean
get() = arrayElementType() != null
fun ConeKotlinType.createOutArrayType(nullable: Boolean = false, createPrimitiveArrayType: Boolean = true): ConeKotlinType {
return ConeKotlinTypeProjectionOut(this).createArrayType(nullable, createPrimitiveArrayType)
}
fun ConeTypeProjection.createArrayType(nullable: Boolean = false, createPrimitiveArrayTypeIfPossible: Boolean = true): ConeClassLikeType {
if (this is ConeKotlinTypeProjection && createPrimitiveArrayTypeIfPossible) {
val type = type.lowerBoundIfFlexible()
if (type is ConeClassLikeType && type.nullability != ConeNullability.NULLABLE) {
val classId = type.lookupTag.classId
val primitiveArrayId =
StandardClassIds.primitiveArrayTypeByElementType[classId] ?: StandardClassIds.unsignedArrayTypeByElementType[classId]
if (primitiveArrayId != null) {
return primitiveArrayId.constructClassLikeType(emptyArray(), nullable)
}
}
}
return StandardClassIds.Array.constructClassLikeType(arrayOf(this), nullable)
}
fun ConeKotlinType.arrayElementType(): ConeKotlinType? {
val type = this.lowerBoundIfFlexible()
if (type !is ConeClassLikeType) return null
val classId = type.lookupTag.classId
if (classId == StandardClassIds.Array)
return (type.typeArguments.first() as ConeKotlinTypeProjection).type
val elementType = StandardClassIds.elementTypeByPrimitiveArrayType[classId] ?: StandardClassIds.elementTypeByUnsignedArrayType[classId]
if (elementType != null) {
return elementType.constructClassLikeType(emptyArray(), isNullable = false)
}
return null
}
fun ConeKotlinType.varargElementType(): ConeKotlinType {
return this.arrayElementType() ?: this
}
fun ConeKotlinType?.isPotentiallyArray(): Boolean =
this != null && (this.arrayElementType() != null || this is ConeTypeVariableType)
@@ -0,0 +1,39 @@
/*
* Copyright 2010-2021 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.name.ClassId
import org.jetbrains.kotlin.name.StandardClassIds
object ConeFlexibleTypeBoundsChecker {
private val baseTypesToMutableEquivalent = mapOf(
StandardClassIds.Iterable to StandardClassIds.MutableIterable,
StandardClassIds.Iterator to StandardClassIds.MutableIterator,
StandardClassIds.ListIterator to StandardClassIds.MutableListIterator,
StandardClassIds.List to StandardClassIds.MutableList,
StandardClassIds.Collection to StandardClassIds.MutableCollection,
StandardClassIds.Set to StandardClassIds.MutableSet,
StandardClassIds.Map to StandardClassIds.MutableMap,
StandardClassIds.MapEntry to StandardClassIds.MutableMapEntry
)
private val mutableToBaseMap = baseTypesToMutableEquivalent.entries.associateBy({ it.value }) { it.key }
fun areTypesMayBeLowerAndUpperBoundsOfSameFlexibleTypeByMutability(a: ConeKotlinType, b: ConeKotlinType): Boolean {
val classId = a.classId ?: return false
val possiblePairBound = (baseTypesToMutableEquivalent[classId] ?: mutableToBaseMap[classId]) ?: return false
return possiblePairBound == b.classId
}
// We consider base bounds as not mutable collections
fun getBaseBoundFqNameByMutability(a: ConeKotlinType): ClassId? {
val classId = a.classId ?: return null
if (classId in baseTypesToMutableEquivalent) return classId
return mutableToBaseMap[classId]
}
}
@@ -0,0 +1,520 @@
/*
* Copyright 2010-2021 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.diagnostics.ConeIntermediateDiagnostic
import org.jetbrains.kotlin.fir.isPrimitiveNumberOrUnsignedNumberType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.NoSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.createTypeSubstitutorByTypeConstructor
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.AbstractTypeRefiner
import org.jetbrains.kotlin.types.TypeCheckerState
import org.jetbrains.kotlin.types.model.*
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeContext {
val symbolProvider: FirSymbolProvider get() = session.symbolProvider
override fun nullableNothingType(): ConeClassLikeType {
return session.builtinTypes.nullableNothingType.type
}
override fun nullableAnyType(): ConeClassLikeType {
return session.builtinTypes.nullableAnyType.type
}
override fun nothingType(): ConeClassLikeType {
return session.builtinTypes.nothingType.type
}
override fun anyType(): ConeClassLikeType {
return session.builtinTypes.anyType.type
}
override fun createFlexibleType(lowerBound: SimpleTypeMarker, upperBound: SimpleTypeMarker): KotlinTypeMarker {
require(lowerBound is ConeKotlinType)
require(upperBound is ConeKotlinType)
return coneFlexibleOrSimpleType(this, lowerBound, upperBound)
}
override fun createSimpleType(
constructor: TypeConstructorMarker,
arguments: List<TypeArgumentMarker>,
nullable: Boolean,
isExtensionFunction: Boolean,
annotations: List<AnnotationMarker>?
): SimpleTypeMarker {
val attributesList = annotations?.filterIsInstanceTo<ConeAttribute<*>, MutableList<ConeAttribute<*>>>(mutableListOf())
val attributes: ConeAttributes = if (isExtensionFunction) {
require(constructor is ConeClassLikeLookupTag && constructor.isBuiltinFunctionalType())
// We don't want to create new instance of ConeAttributes which
// contains only CompilerConeAttributes.ExtensionFunctionType
// to avoid memory consumption
if (attributesList != null) {
attributesList += CompilerConeAttributes.ExtensionFunctionType
ConeAttributes.create(attributesList)
} else {
ConeAttributes.WithExtensionFunctionType
}
} else {
attributesList?.let { ConeAttributes.create(it) } ?: ConeAttributes.Empty
}
@Suppress("UNCHECKED_CAST")
return when (constructor) {
is ConeClassLikeLookupTag -> ConeClassLikeTypeImpl(
constructor,
(arguments as List<ConeTypeProjection>).toTypedArray(),
nullable,
attributes,
)
is ConeTypeParameterLookupTag -> ConeTypeParameterTypeImpl(
constructor,
nullable,
attributes
)
else -> error("!")
}
}
override fun createTypeArgument(type: KotlinTypeMarker, variance: TypeVariance): TypeArgumentMarker {
require(type is ConeKotlinType)
return when (variance) {
TypeVariance.INV -> type
TypeVariance.IN -> ConeKotlinTypeProjectionIn(type)
TypeVariance.OUT -> ConeKotlinTypeProjectionOut(type)
}
}
override fun createStarProjection(typeParameter: TypeParameterMarker): TypeArgumentMarker {
return ConeStarProjection
}
override fun newTypeCheckerState(
errorTypesEqualToAnything: Boolean,
stubTypesEqualToAnything: Boolean
): TypeCheckerState = TypeCheckerState(
errorTypesEqualToAnything,
stubTypesEqualToAnything,
allowedTypeVariable = true,
typeSystemContext = this,
kotlinTypePreparator = ConeTypePreparator(session),
kotlinTypeRefiner = AbstractTypeRefiner.Default
)
override fun KotlinTypeMarker.canHaveUndefinedNullability(): Boolean {
require(this is ConeKotlinType)
return this is ConeCapturedType || this is ConeTypeVariableType
|| this is ConeTypeParameterType
}
override fun SimpleTypeMarker.isExtensionFunction(): Boolean {
require(this is ConeKotlinType)
return this.isExtensionFunctionType
}
override fun KotlinTypeMarker.typeDepth() = when (this) {
is ConeSimpleKotlinType -> typeDepth()
is ConeFlexibleType -> maxOf(lowerBound().typeDepth(), upperBound().typeDepth())
else -> error("Type should be simple or flexible: $this")
}
override fun SimpleTypeMarker.typeDepth(): Int {
require(this is ConeKotlinType)
// if (this is TypeUtils.SpecialType) return 0 // TODO: WTF?
if (this is ConeClassLikeType) {
val fullyExpanded = fullyExpandedType(session)
if (this !== fullyExpanded) {
return fullyExpanded.typeDepth()
}
}
var maxArgumentDepth = 0
for (arg in typeArguments) {
val current = if (arg is ConeStarProjection) 1 else (arg as ConeKotlinTypeProjection).type.typeDepth()
if (current > maxArgumentDepth) {
maxArgumentDepth = current
}
}
return maxArgumentDepth + 1
}
override fun KotlinTypeMarker.contains(predicate: (KotlinTypeMarker) -> Boolean): Boolean {
return this.containsInternal(predicate)
}
private fun KotlinTypeMarker?.containsInternal(
predicate: (KotlinTypeMarker) -> Boolean,
visited: HashSet<KotlinTypeMarker> = hashSetOf()
): Boolean {
if (this == null) return false
if (!visited.add(this)) return false
if (predicate(this)) return true
val flexibleType = this as? ConeFlexibleType
if (flexibleType != null
&& (flexibleType.lowerBound.containsInternal(predicate, visited)
|| flexibleType.upperBound.containsInternal(predicate, visited))
) {
return true
}
if (this is ConeDefinitelyNotNullType
&& this.original.containsInternal(predicate, visited)
) {
return true
}
if (this is ConeIntersectionType) {
return this.intersectedTypes.any { it.containsInternal(predicate, visited) }
}
repeat(argumentsCount()) { index ->
val argument = getArgument(index)
if (!argument.isStarProjection() && argument.getType().containsInternal(predicate, visited)) return true
}
return false
}
override fun TypeConstructorMarker.isUnitTypeConstructor(): Boolean {
return this is ConeClassLikeLookupTag && this.classId == StandardClassIds.Unit
}
override fun Collection<KotlinTypeMarker>.singleBestRepresentative(): KotlinTypeMarker? {
if (this.size == 1) return this.first()
val context = newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = true)
return this.firstOrNull { candidate ->
this.all { other ->
// We consider error types equal to anything here, so that intersections like
// {Array<String>, Array<[ERROR]>} work correctly
candidate == other || AbstractTypeChecker.equalTypes(context, candidate, other)
}
}
}
override fun KotlinTypeMarker.isUnit(): Boolean {
require(this is ConeKotlinType)
return this.typeConstructor().isUnitTypeConstructor() && !this.isNullable
}
override fun KotlinTypeMarker.isBuiltinFunctionalTypeOrSubtype(): Boolean {
require(this is ConeKotlinType)
return this.isTypeOrSubtypeOf {
(it.lowerBoundIfFlexible() as ConeKotlinType).isBuiltinFunctionalType(session)
}
}
override fun KotlinTypeMarker.withNullability(nullable: Boolean): KotlinTypeMarker {
require(this is ConeKotlinType)
return this.withNullability(ConeNullability.create(nullable), this@ConeInferenceContext)
}
override fun KotlinTypeMarker.makeDefinitelyNotNullOrNotNull(): KotlinTypeMarker {
require(this is ConeKotlinType)
return makeConeTypeDefinitelyNotNullOrNotNull(this@ConeInferenceContext)
}
override fun SimpleTypeMarker.makeSimpleTypeDefinitelyNotNullOrNotNull(): SimpleTypeMarker {
require(this is ConeKotlinType)
return makeConeTypeDefinitelyNotNullOrNotNull(this@ConeInferenceContext) as SimpleTypeMarker
}
override fun createCapturedType(
constructorProjection: TypeArgumentMarker,
constructorSupertypes: List<KotlinTypeMarker>,
lowerType: KotlinTypeMarker?,
captureStatus: CaptureStatus
): CapturedTypeMarker {
require(lowerType is ConeKotlinType?)
require(constructorProjection is ConeTypeProjection)
return ConeCapturedType(
captureStatus,
lowerType,
constructor = ConeCapturedTypeConstructor(constructorProjection, constructorSupertypes.cast())
)
}
override fun createStubTypeForBuilderInference(typeVariable: TypeVariableMarker): StubTypeMarker {
require(typeVariable is ConeTypeVariable) { "$typeVariable should subtype of ${ConeTypeVariable::class.qualifiedName}" }
return ConeStubTypeForBuilderInference(typeVariable, ConeNullability.create(typeVariable.defaultType().isMarkedNullable()))
}
override fun createStubTypeForTypeVariablesInSubtyping(typeVariable: TypeVariableMarker): StubTypeMarker {
require(typeVariable is ConeTypeVariable) { "$typeVariable should subtype of ${ConeTypeVariable::class.qualifiedName}" }
return ConeStubTypeForTypeVariableInSubtyping(typeVariable, ConeNullability.create(typeVariable.defaultType().isMarkedNullable()))
}
override fun KotlinTypeMarker.removeAnnotations(): KotlinTypeMarker {
require(this is ConeKotlinType)
return withAttributes(ConeAttributes.Empty, this@ConeInferenceContext)
}
override fun SimpleTypeMarker.replaceArguments(newArguments: List<TypeArgumentMarker>): SimpleTypeMarker {
require(this is ConeKotlinType)
return this.withArguments(newArguments.cast<List<ConeTypeProjection>>().toTypedArray(), this@ConeInferenceContext)
}
override fun SimpleTypeMarker.replaceArguments(replacement: (TypeArgumentMarker) -> TypeArgumentMarker): SimpleTypeMarker {
require(this is ConeKotlinType)
return this.withArguments({ replacement(it).cast() }, this@ConeInferenceContext)
}
override fun KotlinTypeMarker.hasExactAnnotation(): Boolean {
require(this is ConeKotlinType)
return attributes.exact != null
}
override fun KotlinTypeMarker.hasNoInferAnnotation(): Boolean {
require(this is ConeKotlinType)
return attributes.noInfer != null
}
override fun TypeVariableMarker.freshTypeConstructor(): TypeConstructorMarker {
require(this is ConeTypeVariable)
return this.typeConstructor
}
override fun CapturedTypeMarker.typeConstructorProjection(): TypeArgumentMarker {
require(this is ConeCapturedType)
return this.constructor.projection
}
override fun CapturedTypeMarker.typeParameter(): TypeParameterMarker? {
require(this is ConeCapturedType)
return this.constructor.typeParameterMarker
}
override fun CapturedTypeMarker.withNotNullProjection(): KotlinTypeMarker {
require(this is ConeCapturedType)
return ConeCapturedType(captureStatus, lowerType, nullability, constructor, attributes, isProjectionNotNull = true)
}
override fun CapturedTypeMarker.isProjectionNotNull(): Boolean {
require(this is ConeCapturedType)
return isProjectionNotNull
}
override fun DefinitelyNotNullTypeMarker.original(): SimpleTypeMarker {
require(this is ConeDefinitelyNotNullType)
return this.original as SimpleTypeMarker
}
override fun typeSubstitutorByTypeConstructor(map: Map<TypeConstructorMarker, KotlinTypeMarker>): ConeSubstitutor {
@Suppress("UNCHECKED_CAST")
return createTypeSubstitutorByTypeConstructor(map as Map<TypeConstructorMarker, ConeKotlinType>, this)
}
override fun createEmptySubstitutor(): ConeSubstitutor {
return ConeSubstitutor.Empty
}
override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker {
if (this === NoSubstitutor) return type
require(this is ConeSubstitutor)
require(type is ConeKotlinType)
return this.substituteOrSelf(type)
}
override fun TypeVariableMarker.defaultType(): SimpleTypeMarker {
require(this is ConeTypeVariable)
return this.defaultType
}
override fun KotlinTypeMarker.isSpecial(): Boolean {
// Cone type system doesn't have special types
return false
}
override fun TypeConstructorMarker.isTypeVariable(): Boolean {
return this is ConeTypeVariableTypeConstructor
}
override fun TypeVariableTypeConstructorMarker.isContainedInInvariantOrContravariantPositions(): Boolean {
require(this is ConeTypeVariableTypeConstructor)
return isContainedInInvariantOrContravariantPositions
}
override fun createErrorType(debugName: String): ConeClassErrorType {
return ConeClassErrorType(ConeIntermediateDiagnostic(debugName))
}
override fun createErrorTypeWithCustomConstructor(debugName: String, constructor: TypeConstructorMarker): KotlinTypeMarker {
return ConeKotlinErrorType(ConeIntermediateDiagnostic("$debugName c: $constructor"))
}
override fun CapturedTypeMarker.captureStatus(): CaptureStatus {
require(this is ConeCapturedType)
return this.captureStatus
}
override fun CapturedTypeMarker.isOldCapturedType(): Boolean = false
override fun TypeConstructorMarker.isCapturedTypeConstructor(): Boolean {
return this is ConeCapturedTypeConstructor
}
override fun TypeConstructorMarker.isTypeParameterTypeConstructor(): Boolean {
return this.getTypeParameterClassifier() != null
}
override fun KotlinTypeMarker.removeExactAnnotation(): KotlinTypeMarker {
require(this is ConeKotlinType)
return withAttributes(attributes.remove(CompilerConeAttributes.Exact), this@ConeInferenceContext)
}
override fun TypeConstructorMarker.toErrorType(): SimpleTypeMarker {
if (this is ErrorTypeConstructor) return createErrorType(reason)
if (this is ConeClassLikeLookupTag) return createErrorType("Not found classifier: $classId")
return createErrorType("Unknown reason")
}
override fun findCommonIntegerLiteralTypesSuperType(explicitSupertypes: List<SimpleTypeMarker>): SimpleTypeMarker? {
return ConeIntegerLiteralTypeImpl.findCommonSuperType(explicitSupertypes)
}
override fun TypeConstructorMarker.getApproximatedIntegerLiteralType(): KotlinTypeMarker {
require(this is ConeIntegerLiteralType)
return this.getApproximatedType()
}
override fun KotlinTypeMarker.isSignedOrUnsignedNumberType(): Boolean {
require(this is ConeKotlinType)
if (this is ConeIntegerLiteralType) return true
if (this !is ConeClassLikeType) return false
return isPrimitiveNumberOrUnsignedNumberType()
}
override fun KotlinTypeMarker.isFunctionOrKFunctionWithAnySuspendability(): Boolean {
require(this is ConeKotlinType)
return this.isBuiltinFunctionalType(session)
}
private fun ConeKotlinType.isTypeOrSubtypeOf(predicate: (ConeKotlinType) -> Boolean): Boolean {
return predicate(this) || DFS.dfsFromNode(
this,
{
// FIXME supertypes of type constructor contain unsubstituted arguments
it.typeConstructor().supertypes()
},
DFS.VisitedWithSet(),
object : DFS.AbstractNodeHandler<ConeKotlinType, Boolean>() {
private var result = false
override fun beforeChildren(current: ConeKotlinType): Boolean {
if (predicate(current)) {
result = true
}
return !result
}
override fun result() = result
}
)
}
override fun KotlinTypeMarker.isSuspendFunctionTypeOrSubtype(): Boolean {
require(this is ConeKotlinType)
return isTypeOrSubtypeOf {
(it.lowerBoundIfFlexible() as ConeKotlinType).isSuspendFunctionType(session)
}
}
override fun KotlinTypeMarker.isExtensionFunctionType(): Boolean {
require(this is ConeKotlinType)
return this.lowerBoundIfFlexible().safeAs<ConeKotlinType>()?.isExtensionFunctionType(session) == true
}
@OptIn(ExperimentalStdlibApi::class)
override fun KotlinTypeMarker.extractArgumentsForFunctionalTypeOrSubtype(): List<KotlinTypeMarker> {
val builtInFunctionalType = getFunctionalTypeFromSupertypes().cast<ConeKotlinType>()
return buildList {
// excluding return type
for (index in 0 until builtInFunctionalType.argumentsCount() - 1) {
add(builtInFunctionalType.getArgument(index).getType())
}
}
}
override fun KotlinTypeMarker.getFunctionalTypeFromSupertypes(): KotlinTypeMarker {
require(this is ConeKotlinType)
assert(this.isBuiltinFunctionalTypeOrSubtype()) {
"Not a function type or subtype: ${this.render()}"
}
return fullyExpandedType(session).let {
val simpleType = it.lowerBoundIfFlexible()
if ((simpleType as ConeKotlinType).isBuiltinFunctionalType(session))
this
else {
var functionalSupertype: KotlinTypeMarker? = null
simpleType.anySuperTypeConstructor { typeConstructor ->
simpleType.fastCorrespondingSupertypes(typeConstructor)?.any { superType ->
val isFunctional = superType.cast<ConeKotlinType>().isBuiltinFunctionalType(session)
if (isFunctional)
functionalSupertype = superType
isFunctional
} ?: false
}
functionalSupertype ?: error("Failed to find functional supertype for $simpleType")
}
}
}
override fun getFunctionTypeConstructor(parametersNumber: Int, isSuspend: Boolean): TypeConstructorMarker {
val classId = if (isSuspend) {
StandardClassIds.SuspendFunctionN(parametersNumber)
} else {
StandardClassIds.FunctionN(parametersNumber)
}
return session.symbolProvider.getClassLikeSymbolByClassId(classId)?.toLookupTag()
?: error("Can't find Function type")
}
override fun getKFunctionTypeConstructor(parametersNumber: Int, isSuspend: Boolean): TypeConstructorMarker {
val classId = if (isSuspend) {
StandardClassIds.KSuspendFunctionN(parametersNumber)
} else {
StandardClassIds.KFunctionN(parametersNumber)
}
return session.symbolProvider.getClassLikeSymbolByClassId(classId)?.toLookupTag()
?: error("Can't find KFunction type")
}
override fun createTypeWithAlternativeForIntersectionResult(
firstCandidate: KotlinTypeMarker,
secondCandidate: KotlinTypeMarker
): KotlinTypeMarker {
require(firstCandidate is ConeKotlinType)
require(secondCandidate is ConeKotlinType)
val intersectionType = firstCandidate.lowerBoundIfFlexible() as? ConeIntersectionType ?: error {
"Expected type is intersection, found $firstCandidate"
}
return intersectionType.withAlternative(secondCandidate)
}
override fun SimpleTypeMarker.createConstraintPartForLowerBoundAndFlexibleTypeVariable(): KotlinTypeMarker =
createFlexibleType(this.makeSimpleTypeDefinitelyNotNullOrNotNull(), this.withNullability(true))
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2021 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.types.AbstractNullabilityChecker
import org.jetbrains.kotlin.types.TypeCheckerState
object ConeNullabilityChecker {
fun isSubtypeOfAny(context: ConeTypeContext, type: ConeKotlinType): Boolean {
val actualType = with(context) { type.lowerBoundIfFlexible() }
return with(AbstractNullabilityChecker) {
context.newTypeCheckerState(errorTypesEqualToAnything = false, stubTypesEqualToAnything = true)
.hasNotNullSupertype(actualType, TypeCheckerState.SupertypesPolicy.LowerIfFlexible)
}
}
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2021 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.config.LanguageVersionSettings
import org.jetbrains.kotlin.types.AbstractTypeApproximator
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
class ConeTypeApproximator(inferenceContext: ConeInferenceContext, languageVersionSettings: LanguageVersionSettings) :
AbstractTypeApproximator(inferenceContext, languageVersionSettings) {
fun approximateToSuperType(type: ConeKotlinType, conf: TypeApproximatorConfiguration): ConeKotlinType? {
return super.approximateToSuperType(type, conf) as ConeKotlinType?
}
fun approximateToSubType(type: ConeKotlinType, conf: TypeApproximatorConfiguration): ConeKotlinType? {
return super.approximateToSubType(type, conf) as ConeKotlinType?
}
}
@@ -0,0 +1,595 @@
/*
* Copyright 2010-2021 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.PrimitiveType
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.resolve.directExpansionType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
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.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.types.TypeCheckerState
import org.jetbrains.kotlin.types.TypeCheckerState.SupertypesPolicy.DoCustomTransform
import org.jetbrains.kotlin.types.TypeCheckerState.SupertypesPolicy.LowerIfFlexible
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
import org.jetbrains.kotlin.types.model.*
class ErrorTypeConstructor(val reason: String) : TypeConstructorMarker {
override fun toString(): String = reason
}
interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, TypeCheckerProviderContext, TypeSystemCommonBackendContext {
val session: FirSession
override fun TypeConstructorMarker.isIntegerLiteralTypeConstructor(): Boolean {
return this is ConeIntegerLiteralType
}
override fun TypeConstructorMarker.isLocalType(): Boolean {
if (this !is ConeClassLikeLookupTag) return false
return classId.isLocal
}
override val TypeVariableTypeConstructorMarker.typeParameter: TypeParameterMarker?
get() {
require(this is ConeTypeVariableTypeConstructor)
return this.originalTypeParameter
}
override fun SimpleTypeMarker.possibleIntegerTypes(): Collection<KotlinTypeMarker> {
return (this as? ConeIntegerLiteralType)?.possibleTypes ?: emptyList()
}
override fun SimpleTypeMarker.fastCorrespondingSupertypes(constructor: TypeConstructorMarker): List<SimpleTypeMarker>? {
require(this is ConeKotlinType)
return session.correspondingSupertypesCache.getCorrespondingSupertypes(this, constructor)
}
override fun SimpleTypeMarker.isIntegerLiteralType(): Boolean {
return this is ConeIntegerLiteralType
}
override fun KotlinTypeMarker.asSimpleType(): SimpleTypeMarker? {
assert(this is ConeKotlinType)
return when (this) {
is ConeClassLikeType -> fullyExpandedType(session)
is ConeSimpleKotlinType -> this
is ConeFlexibleType -> null
else -> error("Unknown simpleType: $this")
}
}
override fun KotlinTypeMarker.asFlexibleType(): FlexibleTypeMarker? {
assert(this is ConeKotlinType)
return this as? ConeFlexibleType
}
override fun KotlinTypeMarker.isError(): Boolean {
assert(this is ConeKotlinType)
return this is ConeClassErrorType || this is ConeKotlinErrorType || this.typeConstructor().isError() ||
(this is ConeClassLikeType && this.lookupTag is ConeClassLikeErrorLookupTag)
}
override fun KotlinTypeMarker.isUninferredParameter(): Boolean {
assert(this is ConeKotlinType)
return this is ConeClassErrorType && this.isUninferredParameter
}
override fun FlexibleTypeMarker.asDynamicType(): DynamicTypeMarker? {
assert(this is ConeKotlinType)
return null // TODO
}
override fun FlexibleTypeMarker.asRawType(): RawTypeMarker? {
require(this is ConeFlexibleType)
return this as? ConeRawType
}
override fun FlexibleTypeMarker.upperBound(): SimpleTypeMarker {
require(this is ConeFlexibleType)
return this.upperBound as SimpleTypeMarker
}
override fun FlexibleTypeMarker.lowerBound(): SimpleTypeMarker {
require(this is ConeFlexibleType)
return this.lowerBound as SimpleTypeMarker
}
override fun SimpleTypeMarker.asCapturedType(): CapturedTypeMarker? {
return this as? ConeCapturedType
}
override fun SimpleTypeMarker.asDefinitelyNotNullType(): DefinitelyNotNullTypeMarker? {
require(this is ConeKotlinType)
return this as? ConeDefinitelyNotNullType
}
override fun SimpleTypeMarker.isMarkedNullable(): Boolean {
require(this is ConeKotlinType)
return this.nullability.isNullable
}
override fun SimpleTypeMarker.withNullability(nullable: Boolean): SimpleTypeMarker {
require(this is ConeKotlinType)
return withNullability(ConeNullability.create(nullable), session.typeContext)
}
override fun SimpleTypeMarker.typeConstructor(): TypeConstructorMarker {
return when (this) {
is ConeClassLikeType -> lookupTag
is ConeTypeParameterType -> lookupTag
is ConeCapturedType -> constructor
is ConeTypeVariableType -> lookupTag
is ConeIntersectionType -> this
is ConeStubType -> variable.typeConstructor
is ConeDefinitelyNotNullType -> original.typeConstructor()
is ConeIntegerLiteralType -> this
else -> error("?: $this")
}
}
override fun CapturedTypeMarker.typeConstructor(): CapturedTypeConstructorMarker {
require(this is ConeCapturedType)
return this.constructor
}
override fun CapturedTypeMarker.captureStatus(): CaptureStatus {
require(this is ConeCapturedType)
return this.captureStatus
}
override fun CapturedTypeMarker.isOldCapturedType(): Boolean = false
override fun CapturedTypeConstructorMarker.projection(): TypeArgumentMarker {
require(this is ConeCapturedTypeConstructor)
return this.projection
}
override fun KotlinTypeMarker.argumentsCount(): Int {
require(this is ConeKotlinType)
return this.typeArguments.size
}
override fun KotlinTypeMarker.getArgument(index: Int): TypeArgumentMarker {
require(this is ConeKotlinType)
return this.typeArguments.getOrNull(index) ?: ConeStarProjection
}
override fun KotlinTypeMarker.getArguments(): List<TypeArgumentMarker> {
require(this is ConeKotlinType)
return this.typeArguments.toList()
}
override fun KotlinTypeMarker.asTypeArgument(): TypeArgumentMarker {
require(this is ConeKotlinType)
return this
}
override fun CapturedTypeMarker.lowerType(): KotlinTypeMarker? {
require(this is ConeCapturedType)
if (!this.isMarkedNullable) return this.lowerType
return this.lowerType?.makeNullable()
}
override fun TypeArgumentMarker.isStarProjection(): Boolean {
require(this is ConeTypeProjection)
return this is ConeStarProjection
}
override fun TypeArgumentMarker.getVariance(): TypeVariance {
require(this is ConeTypeProjection)
return when (this.kind) {
ProjectionKind.STAR -> error("Nekorrektno (c) Stas")
ProjectionKind.IN -> TypeVariance.IN
ProjectionKind.OUT -> TypeVariance.OUT
ProjectionKind.INVARIANT -> TypeVariance.INV
}
}
override fun TypeArgumentMarker.getType(): KotlinTypeMarker {
require(this is ConeTypeProjection)
require(this is ConeKotlinTypeProjection) { "No type for StarProjection" }
return this.type
}
override fun TypeConstructorMarker.parametersCount(): Int {
return when (this) {
is ConeTypeParameterLookupTag,
is ConeCapturedTypeConstructor,
is ErrorTypeConstructor,
is ConeTypeVariableTypeConstructor,
is ConeIntersectionType -> 0
is ConeClassLikeLookupTag -> {
when (val symbol = toSymbol(session)) {
is FirAnonymousObjectSymbol -> symbol.fir.typeParameters.size
is FirRegularClassSymbol -> symbol.fir.typeParameters.size
is FirTypeAliasSymbol -> symbol.fir.typeParameters.size
else -> 0
}
}
is ConeIntegerLiteralType -> 0
else -> unknownConstructorError()
}
}
override fun TypeConstructorMarker.getParameter(index: Int): TypeParameterMarker {
return when (val symbol = toClassLikeSymbol()) {
is FirAnonymousObjectSymbol -> symbol.fir.typeParameters[index].symbol.toLookupTag()
is FirRegularClassSymbol -> symbol.fir.typeParameters[index].symbol.toLookupTag()
is FirTypeAliasSymbol -> symbol.fir.typeParameters[index].symbol.toLookupTag()
else -> error("Unexpected FirClassLikeSymbol $symbol for ${this::class}, with classId ${(this as? ConeClassLikeLookupTag)?.classId}")
}
}
override fun TypeConstructorMarker.getParameters(): List<TypeParameterMarker> {
return when (val symbol = toClassLikeSymbol()) {
is FirAnonymousObjectSymbol -> symbol.fir.typeParameters.map { it.symbol.toLookupTag() }
is FirRegularClassSymbol -> symbol.fir.typeParameters.map { it.symbol.toLookupTag() }
is FirTypeAliasSymbol -> symbol.fir.typeParameters.map { it.symbol.toLookupTag() }
else -> emptyList()
}
}
private fun TypeConstructorMarker.toClassLikeSymbol(): FirClassLikeSymbol<*>? = (this as? ConeClassLikeLookupTag)?.toSymbol(session)
override fun TypeConstructorMarker.supertypes(): Collection<ConeKotlinType> {
if (this is ErrorTypeConstructor) return emptyList()
return when (this) {
is ConeTypeVariableTypeConstructor -> emptyList()
is ConeTypeParameterLookupTag -> symbol.fir.bounds.map { it.coneType }
is ConeClassLikeLookupTag -> {
when (val symbol = toClassLikeSymbol().also { it?.ensureResolved(FirResolvePhase.TYPES) }) {
is FirClassSymbol<*> -> symbol.fir.superConeTypes
is FirTypeAliasSymbol -> listOfNotNull(symbol.fir.expandedConeType)
else -> listOf(session.builtinTypes.anyType.type)
}
}
is ConeCapturedTypeConstructor -> supertypes!!
is ConeIntersectionType -> intersectedTypes
is ConeIntegerLiteralType -> supertypes
else -> unknownConstructorError()
}
}
override fun TypeConstructorMarker.isIntersection(): Boolean {
return this is ConeIntersectionType
}
override fun TypeConstructorMarker.isClassTypeConstructor(): Boolean {
return this is ConeClassLikeLookupTag
}
override fun TypeConstructorMarker.isInterface(): Boolean {
return ((this as? ConeClassLikeLookupTag)?.toClassLikeSymbol()?.fir as? FirClass)?.classKind == ClassKind.INTERFACE
}
override fun TypeParameterMarker.getVariance(): TypeVariance {
require(this is ConeTypeParameterLookupTag)
return this.symbol.fir.variance.convertVariance()
}
override fun TypeParameterMarker.upperBoundCount(): Int {
require(this is ConeTypeParameterLookupTag)
return this.symbol.fir.bounds.size
}
override fun TypeParameterMarker.getUpperBound(index: Int): KotlinTypeMarker {
require(this is ConeTypeParameterLookupTag)
return this.symbol.fir.bounds[index].coneType
}
override fun TypeParameterMarker.getUpperBounds(): List<KotlinTypeMarker> {
require(this is ConeTypeParameterLookupTag)
return this.symbol.fir.bounds.map { it.coneType }
}
override fun TypeParameterMarker.getTypeConstructor(): TypeConstructorMarker {
require(this is ConeTypeParameterLookupTag)
return this
}
override fun TypeParameterMarker.hasRecursiveBounds(selfConstructor: TypeConstructorMarker?): Boolean {
require(this is ConeTypeParameterLookupTag)
return this.typeParameterSymbol.fir.bounds.any { typeRef ->
typeRef.coneType.contains { it.typeConstructor() == this.getTypeConstructor() }
&& (selfConstructor == null || typeRef.coneType.typeConstructor() == selfConstructor)
}
}
override fun areEqualTypeConstructors(c1: TypeConstructorMarker, c2: TypeConstructorMarker): Boolean {
if (c1 is ErrorTypeConstructor || c2 is ErrorTypeConstructor) return false
return c1 == c2
}
override fun TypeConstructorMarker.isDenotable(): Boolean {
return when (this) {
is ConeClassLikeLookupTag,
is ConeTypeParameterLookupTag -> true
is ConeCapturedTypeConstructor,
is ErrorTypeConstructor,
is ConeTypeVariableTypeConstructor,
is ConeIntegerLiteralType,
is ConeIntersectionType -> false
else -> unknownConstructorError()
}
}
override fun TypeConstructorMarker.isCommonFinalClassConstructor(): Boolean {
val symbol = toClassLikeSymbol() ?: return false
if (symbol is FirAnonymousObjectSymbol) return true
val classSymbol = symbol as? FirRegularClassSymbol ?: return false
val fir = classSymbol.fir
return fir.modality == Modality.FINAL &&
fir.classKind != ClassKind.ENUM_ENTRY &&
fir.classKind != ClassKind.ANNOTATION_CLASS
}
override fun captureFromExpression(type: KotlinTypeMarker): KotlinTypeMarker? {
require(type is ConeKotlinType)
return captureFromExpressionInternal(type)
}
override fun captureFromArguments(type: SimpleTypeMarker, status: CaptureStatus): SimpleTypeMarker? {
require(type is ConeKotlinType)
return captureFromArgumentsInternal(type, status) as SimpleTypeMarker?
}
override fun SimpleTypeMarker.asArgumentList(): TypeArgumentListMarker {
require(this is ConeKotlinType)
return this
}
override fun identicalArguments(a: SimpleTypeMarker, b: SimpleTypeMarker): Boolean {
require(a is ConeKotlinType)
require(b is ConeKotlinType)
return a.typeArguments === b.typeArguments
}
override fun TypeConstructorMarker.isAnyConstructor(): Boolean {
return this is ConeClassLikeLookupTag && classId == StandardClassIds.Any
}
override fun TypeConstructorMarker.isNothingConstructor(): Boolean {
return this is ConeClassLikeLookupTag && classId == StandardClassIds.Nothing
}
override fun SimpleTypeMarker.isSingleClassifierType(): Boolean {
if (isError()) return false
if (this is ConeCapturedType) return true
if (this is ConeTypeVariableType) return false
if (this is ConeIntersectionType) return false
if (this is ConeIntegerLiteralType) return true
if (this is ConeStubType) return true
if (this is ConeDefinitelyNotNullType) return true
require(this is ConeLookupTagBasedType)
val typeConstructor = this.typeConstructor()
return typeConstructor is ConeClassLikeLookupTag ||
typeConstructor is ConeTypeParameterLookupTag
}
override fun SimpleTypeMarker.isPrimitiveType(): Boolean {
if (this is ConeClassLikeType) {
return isPrimitive
}
return false
}
override fun KotlinTypeMarker.getAnnotations(): List<AnnotationMarker> {
require(this is ConeKotlinType)
return attributes.toList()
}
override fun SimpleTypeMarker.isStubType(): Boolean {
return this is ConeStubType
}
override fun SimpleTypeMarker.isStubTypeForVariableInSubtyping(): Boolean {
return this is ConeStubTypeForTypeVariableInSubtyping
}
override fun SimpleTypeMarker.isStubTypeForBuilderInference(): Boolean {
return this is ConeStubTypeForBuilderInference
}
override fun intersectTypes(types: List<SimpleTypeMarker>): SimpleTypeMarker {
@Suppress("UNCHECKED_CAST")
return ConeTypeIntersector.intersectTypes(this as ConeInferenceContext, types as List<ConeKotlinType>) as SimpleTypeMarker
}
override fun intersectTypes(types: List<KotlinTypeMarker>): ConeKotlinType {
@Suppress("UNCHECKED_CAST")
return ConeTypeIntersector.intersectTypes(this as ConeInferenceContext, types as List<ConeKotlinType>)
}
override fun KotlinTypeMarker.isNullableType(): Boolean {
require(this is ConeKotlinType)
if (this.isMarkedNullable)
return true
return when (this) {
is ConeFlexibleType -> this.upperBound.isNullableType()
is ConeTypeParameterType -> lookupTag.symbol.allBoundsAreNullable()
is ConeTypeVariableType -> {
val symbol = lookupTag.toSymbol(session) ?: return false
when (symbol) {
is FirClassSymbol -> false
is FirTypeAliasSymbol -> symbol.fir.expandedConeType?.isNullableType() ?: false
is FirTypeParameterSymbol -> symbol.allBoundsAreNullable()
}
}
is ConeIntersectionType -> intersectedTypes.all { it.isNullableType() }
is ConeClassLikeType -> directExpansionType(session)?.isNullableType() ?: false
else -> false
}
}
private fun FirTypeParameterSymbol.allBoundsAreNullable(): Boolean {
return fir.bounds.all { it.coneType.isNullableType() }
}
private fun TypeConstructorMarker.toFirRegularClass(): FirRegularClass? {
return toClassLikeSymbol()?.fir as? FirRegularClass
}
override fun nullableAnyType(): SimpleTypeMarker = session.builtinTypes.nullableAnyType.type
override fun arrayType(componentType: KotlinTypeMarker): SimpleTypeMarker {
require(componentType is ConeKotlinType)
return componentType.createArrayType(nullable = false)
}
override fun KotlinTypeMarker.isArrayOrNullableArray(): Boolean {
require(this is ConeKotlinType)
return this.classId == StandardClassIds.Array
}
override fun TypeConstructorMarker.isFinalClassOrEnumEntryOrAnnotationClassConstructor(): Boolean {
val firRegularClass = toFirRegularClass() ?: return false
return firRegularClass.modality == Modality.FINAL ||
firRegularClass.classKind == ClassKind.ENUM_ENTRY ||
firRegularClass.classKind == ClassKind.ANNOTATION_CLASS
}
override fun KotlinTypeMarker.hasAnnotation(fqName: FqName): Boolean {
require(this is ConeKotlinType)
val compilerAttribute = CompilerConeAttributes.compilerAttributeByFqName[fqName]
if (compilerAttribute != null) {
return compilerAttribute in attributes
}
val customAnnotations = attributes.customAnnotations
return customAnnotations.any {
it.typeRef.coneTypeSafe<ConeKotlinType>()?.fullyExpandedType(session)?.classId?.asSingleFqName() == fqName
}
}
override fun KotlinTypeMarker.getAnnotationFirstArgumentValue(fqName: FqName): Any? {
require(this is ConeKotlinType)
// We don't check for compiler attributes because all of them doesn't have parameters
val customAnnotations = attributes.customAnnotations
val annotationCall = customAnnotations.firstOrNull {
it.typeRef.coneTypeSafe<ConeKotlinType>()?.fullyExpandedType(session)?.classId?.asSingleFqName() == fqName
} ?: return null
val argument = when (val argument = annotationCall.argumentMapping.mapping.values.firstOrNull() ?: return null) {
is FirVarargArgumentsExpression -> argument.arguments.firstOrNull()
is FirArrayOfCall -> argument.arguments.firstOrNull()
is FirNamedArgumentExpression -> argument.expression
else -> argument
} ?: return null
return (argument as? FirConstExpression<*>)?.value
}
override fun TypeConstructorMarker.getTypeParameterClassifier(): TypeParameterMarker? {
return this as? ConeTypeParameterLookupTag
}
override fun TypeConstructorMarker.isInlineClass(): Boolean {
return toFirRegularClass()?.isInline == true
}
override fun TypeConstructorMarker.isInnerClass(): Boolean {
return toFirRegularClass()?.isInner == true
}
override fun TypeParameterMarker.getRepresentativeUpperBound(): KotlinTypeMarker {
require(this is ConeTypeParameterLookupTag)
return this.symbol.fir.bounds.getOrNull(0)?.coneType
?: session.builtinTypes.nullableAnyType.type
}
override fun KotlinTypeMarker.getUnsubstitutedUnderlyingType(): KotlinTypeMarker? {
require(this is ConeKotlinType)
return unsubstitutedUnderlyingTypeForInlineClass(session)
}
override fun KotlinTypeMarker.getSubstitutedUnderlyingType(): KotlinTypeMarker? {
require(this is ConeKotlinType)
return substitutedUnderlyingTypeForInlineClass(session, this@ConeTypeContext)
}
override fun TypeConstructorMarker.getPrimitiveType(): PrimitiveType? =
getClassFqNameUnsafe()?.let(StandardNames.FqNames.fqNameToPrimitiveType::get)
override fun TypeConstructorMarker.getPrimitiveArrayType(): PrimitiveType? =
getClassFqNameUnsafe()?.let(StandardNames.FqNames.arrayClassFqNameToPrimitiveType::get)
override fun TypeConstructorMarker.isUnderKotlinPackage(): Boolean =
getClassFqNameUnsafe()?.startsWith(StandardClassIds.BASE_KOTLIN_PACKAGE.shortName()) == true
override fun TypeConstructorMarker.getClassFqNameUnsafe(): FqNameUnsafe? {
if (this !is ConeClassLikeLookupTag) return null
return classId.asSingleFqName().toUnsafe()
}
override fun TypeParameterMarker.getName() = (this as ConeTypeParameterLookupTag).name
override fun TypeParameterMarker.isReified(): Boolean {
require(this is ConeTypeParameterLookupTag)
return typeParameterSymbol.fir.isReified
}
override fun KotlinTypeMarker.isInterfaceOrAnnotationClass(): Boolean {
val classKind = typeConstructor().toFirRegularClass()?.classKind ?: return false
return classKind == ClassKind.ANNOTATION_CLASS || classKind == ClassKind.INTERFACE
}
override fun TypeConstructorMarker.isError(): Boolean {
return this is ErrorTypeConstructor
}
private fun TypeConstructorMarker.unknownConstructorError(): Nothing {
error("Unknown type constructor: ${this::class}")
}
override fun substitutionSupertypePolicy(type: SimpleTypeMarker): TypeCheckerState.SupertypesPolicy {
if (type.argumentsCount() == 0) return LowerIfFlexible
require(type is ConeKotlinType)
val declaration = when (type) {
is ConeClassLikeType -> type.lookupTag.toSymbol(session)?.fir
else -> null
}
val substitutor = if (declaration is FirTypeParameterRefsOwner) {
val substitution =
declaration.typeParameters.zip(type.typeArguments).associate { (parameter, argument) ->
parameter.symbol to ((argument as? ConeKotlinTypeProjection)?.type
?: session.builtinTypes.nullableAnyType.type)//StandardClassIds.Any(session.firSymbolProvider).constructType(emptyArray(), isNullable = true))
}
substitutorByMap(substitution, session)
} else {
ConeSubstitutor.Empty
}
return object : DoCustomTransform() {
override fun transformType(state: TypeCheckerState, type: KotlinTypeMarker): SimpleTypeMarker {
val lowerBound = type.lowerBoundIfFlexible()
require(lowerBound is ConeKotlinType)
return substitutor.substituteOrSelf(lowerBound) as SimpleTypeMarker
}
}
}
override fun KotlinTypeMarker.isTypeVariableType(): Boolean {
return this is ConeTypeVariableType
}
}
@@ -0,0 +1,165 @@
/*
* Copyright 2010-2021 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.types.AbstractTypeChecker
object ConeTypeIntersector {
fun intersectTypes(
context: ConeInferenceContext,
types: List<ConeKotlinType>
): ConeKotlinType {
when (types.size) {
0 -> error("Expected some types")
1 -> return types.single()
}
val inputTypes = mutableListOf<ConeKotlinType>()
flatIntersectionTypes(types, inputTypes)
/**
* resultNullability. Value description:
* ACCEPT_NULL means that all types marked nullable
*
* NOT_NULL means that there is one type which is subtype of Any => all types can be made definitely not null,
* making types definitely not null (not just not null) makes sense when we have intersection of type parameters like {T!! & S}
*
* UNKNOWN means, that we do not know, i.e. more precisely, all singleClassifier types marked nullable if any,
* and other types is captured types or type parameters without not-null upper bound. Example: `String? & T` such types we should leave as is.
*/
val resultNullability = inputTypes.fold(ResultNullability.START) { nullability, nextType ->
nullability.combine(nextType.type, context)
}
val inputTypesWithCorrectNullability = inputTypes.mapTo(LinkedHashSet()) {
if (resultNullability == ResultNullability.NOT_NULL) with(context) {
it.makeDefinitelyNotNullOrNotNull() as ConeKotlinType
} else it
}
return intersectTypesWithoutIntersectionType(context, inputTypesWithCorrectNullability)
}
private fun intersectTypesWithoutIntersectionType(
context: ConeTypeContext,
inputTypes: Set<ConeKotlinType>
): ConeKotlinType {
if (inputTypes.size == 1) return inputTypes.single().type
// Any and Nothing should leave
// Note that duplicates should be dropped because we have Set here.
val errorMessage = { "This collections cannot be empty! input types: ${inputTypes.joinToString()}" }
val filteredEqualTypes = filterTypes(inputTypes) { lower, upper ->
/*
* Here we drop types from intersection set for cases like that:
*
* interface A
* interface B : A
*
* type = (A & B & ...)
*
* We want to drop A from that set, because it's useless for type checking. But in case if
* A came from inference and B came from smartcast we want to safe both types in intersection
*/
isStrictSupertype(context, lower, upper)
}
assert(filteredEqualTypes.isNotEmpty(), errorMessage)
// TODO
// IntegerLiteralTypeConstructor.findIntersectionType(filteredEqualTypes)?.let { return it }
/*
* For the case like it(ft(String..String?), String?), where ft(String..String?) == String?, we prefer to _keep_ flexible type.
* When a == b, the former, i.e., the one in the list will be filtered out, and the other one will remain.
* So, here, we sort the interim list such that flexible types appear later.
*/
val sortedEqualTypes = filteredEqualTypes.sortedWith { p0, p1 ->
when {
p0 is ConeFlexibleType && p1 is ConeFlexibleType -> 0
p0 is ConeFlexibleType -> 1
p1 is ConeFlexibleType -> -1
else -> 0
}
}
val filteredSuperAndEqualTypes = filterTypes(sortedEqualTypes) { a, b ->
AbstractTypeChecker.equalTypes(context, a, b)
}
assert(filteredSuperAndEqualTypes.isNotEmpty(), errorMessage)
if (filteredSuperAndEqualTypes.size < 2) return filteredSuperAndEqualTypes.single()
return ConeIntersectionType(filteredSuperAndEqualTypes)
}
private fun filterTypes(
inputTypes: Collection<ConeKotlinType>,
predicate: (lower: ConeKotlinType, upper: ConeKotlinType) -> Boolean
): List<ConeKotlinType> {
val filteredTypes = ArrayList(inputTypes)
val iterator = filteredTypes.iterator()
while (iterator.hasNext()) {
val upper = iterator.next()
val shouldFilter = filteredTypes.any { lower -> lower !== upper && predicate(lower, upper) }
if (shouldFilter) iterator.remove()
}
return filteredTypes
}
private fun isStrictSupertype(context: ConeTypeContext, subtype: ConeKotlinType, supertype: ConeKotlinType): Boolean {
return with(AbstractTypeChecker) {
isSubtypeOf(context, subtype, supertype) && !isSubtypeOf(context, supertype, subtype)
}
}
private fun flatIntersectionTypes(
inputTypes: List<ConeKotlinType>,
typeCollector: MutableList<ConeKotlinType>
) {
for (inputType in inputTypes) {
if (inputType is ConeIntersectionType) {
for (type in inputType.intersectedTypes) {
typeCollector += type
}
} else {
typeCollector += inputType
}
}
}
private enum class ResultNullability {
START {
override fun combine(nextType: ConeKotlinType, context: ConeTypeContext): ResultNullability =
nextType.resultNullability(context)
},
ACCEPT_NULL {
override fun combine(nextType: ConeKotlinType, context: ConeTypeContext): ResultNullability =
nextType.resultNullability(context)
},
// example: type parameter without not-null supertype
UNKNOWN {
override fun combine(nextType: ConeKotlinType, context: ConeTypeContext): ResultNullability =
nextType.resultNullability(context).let {
if (it == ACCEPT_NULL) this else it
}
},
NOT_NULL {
override fun combine(nextType: ConeKotlinType, context: ConeTypeContext): ResultNullability = this
};
abstract fun combine(nextType: ConeKotlinType, context: ConeTypeContext): ResultNullability
protected fun ConeKotlinType.resultNullability(context: ConeTypeContext): ResultNullability =
when {
isMarkedNullable -> ACCEPT_NULL
this is ConeFlexibleType -> upperBound.resultNullability(context)
ConeNullabilityChecker.isSubtypeOfAny(context, this) -> NOT_NULL
else -> UNKNOWN
}
}
}
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2021 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.resolve.fullyExpandedType
import org.jetbrains.kotlin.types.AbstractTypePreparator
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
class ConeTypePreparator(val session: FirSession) : AbstractTypePreparator() {
override fun prepareType(type: KotlinTypeMarker): KotlinTypeMarker {
return when (type) {
is ConeClassLikeType -> type.fullyExpandedType(session)
is ConeFlexibleType -> {
val lowerBound = prepareType(type.lowerBound)
if (lowerBound === type.lowerBound) return type
ConeFlexibleType(
lowerBound as ConeKotlinType,
prepareType(type.upperBound) as ConeKotlinType
)
}
else -> type
}
}
}
@@ -0,0 +1,106 @@
/*
* Copyright 2010-2021 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.FirSessionComponent
import org.jetbrains.kotlin.fir.ThreadSafeMutableState
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRefsOwner
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.types.TypeCheckerState
import org.jetbrains.kotlin.types.model.CaptureStatus
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
@ThreadSafeMutableState
class FirCorrespondingSupertypesCache(private val session: FirSession) : FirSessionComponent {
private val cache = HashMap<ConeClassLikeLookupTag, Map<ConeClassLikeLookupTag, List<ConeClassLikeType>>?>(1000, 0.5f)
fun getCorrespondingSupertypes(
type: ConeKotlinType,
supertypeConstructor: TypeConstructorMarker
): List<ConeClassLikeType>? {
if (type !is ConeClassLikeType || supertypeConstructor !is ConeClassLikeLookupTag) return null
val typeContext = session.typeContext
val typeCheckerState = typeContext.newTypeCheckerState(
errorTypesEqualToAnything = false,
stubTypesEqualToAnything = true
)
val lookupTag = type.lookupTag
if (lookupTag == supertypeConstructor) return listOf(captureType(type, typeContext))
if (lookupTag !in cache) {
cache[lookupTag] = computeSupertypesMap(lookupTag, typeCheckerState)
}
val resultTypes = cache[lookupTag]?.getOrDefault(supertypeConstructor, emptyList()) ?: return null
if (type.typeArguments.isEmpty()) return resultTypes
val capturedType = captureType(type, typeContext)
val substitutionSupertypePolicy = typeContext.substitutionSupertypePolicy(capturedType)
return resultTypes.map {
substitutionSupertypePolicy.transformType(typeCheckerState, it) as ConeClassLikeType
}
}
private fun captureType(type: ConeClassLikeType, typeSystemContext: ConeTypeContext): ConeClassLikeType =
(typeSystemContext.captureFromArguments(type, CaptureStatus.FOR_SUBTYPING) ?: type) as ConeClassLikeType
private fun computeSupertypesMap(
subtypeLookupTag: ConeClassLikeLookupTag,
state: TypeCheckerState
): Map<ConeClassLikeLookupTag, List<ConeClassLikeType>>? {
val resultingMap = HashMap<ConeClassLikeLookupTag, List<ConeClassLikeType>>()
val subtypeFirClass: FirClassLikeDeclaration = subtypeLookupTag.toSymbol(session)?.fir ?: return null
val defaultType = subtypeLookupTag.constructClassType(
(subtypeFirClass as? FirTypeParameterRefsOwner)?.typeParameters?.map {
it.symbol.toLookupTag().constructType(emptyArray(), isNullable = false)
}?.toTypedArray().orEmpty(),
isNullable = false
)
if (state.anySupertype(
defaultType,
{ it !is ConeClassLikeType || it.lookupTag.toSymbol(session) !is FirClassLikeSymbol<*> }
) { supertype -> computeSupertypePolicyAndPutInMap(supertype, resultingMap, state) }
) {
return null
}
return resultingMap.also {
it.remove(subtypeLookupTag) // Just optimization: do not preserve mapping from MyClass to MyClas itself
}
}
private fun computeSupertypePolicyAndPutInMap(
supertype: SimpleTypeMarker,
resultingMap: MutableMap<ConeClassLikeLookupTag, List<ConeClassLikeType>>,
state: TypeCheckerState
): TypeCheckerState.SupertypesPolicy {
val supertypeLookupTag = (supertype as ConeClassLikeType).lookupTag
val captured =
state.typeSystemContext.captureFromArguments(supertype, CaptureStatus.FOR_SUBTYPING) as ConeClassLikeType? ?: supertype
resultingMap[supertypeLookupTag] = listOf(captured)
return when {
with(state.typeSystemContext) { captured.argumentsCount() } == 0 -> {
TypeCheckerState.SupertypesPolicy.LowerIfFlexible
}
else -> {
state.typeSystemContext.substitutionSupertypePolicy(captured)
}
}
}
}
val FirSession.correspondingSupertypesCache: FirCorrespondingSupertypesCache by FirSession.sessionComponentAccessor()
@@ -0,0 +1,243 @@
/*
* Copyright 2010-2021 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.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.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.ConeClassLikeLookupTagImpl
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.StandardClassIds
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.util.OperatorNameConventions
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@OptIn(ExperimentalContracts::class)
private fun ConeKotlinType.classId(session: FirSession): ClassId? {
contract {
returns(true) implies (this@classId is ConeClassLikeType)
}
if (this !is ConeClassLikeType) return null
return fullyExpandedType(session).lookupTag.classId
}
fun ConeKotlinType.isKMutableProperty(session: FirSession): Boolean {
val classId = classId(session) ?: return false
return classId.packageFqName == StandardClassIds.BASE_REFLECT_PACKAGE &&
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 }
}
// SuspendFunction, KSuspendFunction
fun ConeKotlinType.isSuspendFunctionType(session: FirSession): Boolean {
return isFunctionalType(session) { it.isSuspendType }
}
// 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 (isSuspendFunctionType(session)) FunctionClassKind.SuspendFunction
else FunctionClassKind.Function
val functionalTypeId = ClassId(kind.packageFqName, kind.numberedClassName(typeArguments.size - 1))
return ConeClassLikeTypeImpl(ConeClassLikeLookupTagImpl(functionalTypeId), typeArguments, isNullable = false)
}
fun ConeKotlinType.suspendFunctionTypeToFunctionType(session: FirSession): ConeClassLikeType {
require(this.isSuspendFunctionType(session))
val kind =
if (isKFunctionType(session)) FunctionClassKind.KFunction
else FunctionClassKind.Function
val functionalTypeId = ClassId(kind.packageFqName, kind.numberedClassName(typeArguments.size - 1))
return ConeClassLikeTypeImpl(ConeClassLikeLookupTagImpl(functionalTypeId), typeArguments, isNullable = false, attributes = attributes)
}
fun ConeKotlinType.suspendFunctionTypeToFunctionTypeWithContinuation(session: FirSession, continuationClassId: ClassId): ConeClassLikeType {
require(this.isSuspendFunctionType(session))
val kind =
if (isKFunctionType(session)) FunctionClassKind.KFunction
else FunctionClassKind.Function
val functionalTypeId = ClassId(kind.packageFqName, kind.numberedClassName(typeArguments.size))
return ConeClassLikeTypeImpl(
ConeClassLikeLookupTagImpl(functionalTypeId),
typeArguments = (type.typeArguments.dropLast(1) + ConeClassLikeLookupTagImpl(continuationClassId).constructClassType(
arrayOf(type.typeArguments.last()),
isNullable = false
) + type.typeArguments.last()).toTypedArray(),
isNullable = false,
attributes = 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.isSuspendFunctionType(session))
return when (this) {
is ConeClassLikeType -> {
// Expect the argument type is not a suspend functional type.
if (isSuspendFunctionType(session) || !isSubtypeOfFunctionalType(session, expectedFunctionalType))
null
else
this
}
is ConeIntersectionType -> {
if (intersectedTypes.any { it.isSuspendFunctionType(session) })
null
else
intersectedTypes.find { it.findSubtypeOfNonSuspendFunctionalType(session, expectedFunctionalType) != null }
}
is ConeTypeParameterType -> {
val bounds = lookupTag.typeParameterSymbol.fir.bounds.map { it.coneType }
if (bounds.any { it.isSuspendFunctionType(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) ?: 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 {
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).typeArguments.first().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.receiverType: ConeKotlinType? get() = receiverTypeRef?.coneTypeSafe()
fun ConeTypeContext.isTypeMismatchDueToNullability(
actualType: ConeKotlinType,
expectedType: ConeKotlinType
): Boolean {
return actualType.isNullableType() && !expectedType.isNullableType() && AbstractTypeChecker.isSubtypeOf(
this,
actualType,
expectedType.withNullability(ConeNullability.NULLABLE, this)
)
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2021 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.FirSessionComponent
import org.jetbrains.kotlin.fir.languageVersionSettings
class TypeComponents(val session: FirSession) : FirSessionComponent {
val typeContext: ConeInferenceContext = object : ConeInferenceContext {
override val session: FirSession
get() = this@TypeComponents.session
}
val typeApproximator: ConeTypeApproximator = ConeTypeApproximator(typeContext, session.languageVersionSettings)
}
private val FirSession.typeComponents: TypeComponents by FirSession.sessionComponentAccessor()
val FirSession.typeContext: ConeInferenceContext
get() = typeComponents.typeContext
val FirSession.typeApproximator: ConeTypeApproximator
get() = typeComponents.typeApproximator
@@ -0,0 +1,556 @@
/*
* Copyright 2010-2021 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.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLookupTagWithFixedSymbol
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.model.*
fun ConeInferenceContext.commonSuperTypeOrNull(types: List<ConeKotlinType>): ConeKotlinType? {
return when (types.size) {
0 -> null
1 -> types.first()
else -> with(NewCommonSuperTypeCalculator) {
commonSuperType(types) as ConeKotlinType
}
}
}
fun ConeInferenceContext.intersectTypesOrNull(types: List<ConeKotlinType>): ConeKotlinType? {
return when (types.size) {
0 -> null
1 -> types.first()
else -> ConeTypeIntersector.intersectTypes(this, types)
}
}
fun TypeCheckerProviderContext.equalTypes(a: ConeKotlinType, b: ConeKotlinType): Boolean =
AbstractTypeChecker.equalTypes(this, a, b)
private fun ConeTypeContext.makesSenseToBeDefinitelyNotNull(type: ConeKotlinType): Boolean = when (type) {
is ConeTypeParameterType -> type.isNullableType()
// Actually, this branch should work for type parameters as well, but it breaks some cases. See KT-40114.
// Basically, if we have `T : X..X?`, then `T <: Any` but we still have `T` != `T & Any`.
is ConeTypeVariableType, is ConeCapturedType ->
!AbstractNullabilityChecker.isSubtypeOfAny(
newTypeCheckerState(errorTypesEqualToAnything = false, stubTypesEqualToAnything = false), type
)
// For all other types `T & Any` is the same as `T` without a question mark.
// TODO: not true for flexible types.
else -> false
}
// TODO: leave only one of `create` and `makeConeTypeDefinitelyNotNullOrNotNull`
fun ConeDefinitelyNotNullType.Companion.create(
original: ConeKotlinType,
typeContext: ConeTypeContext
): ConeDefinitelyNotNullType? {
return when {
original is ConeDefinitelyNotNullType -> original
typeContext.makesSenseToBeDefinitelyNotNull(original) -> ConeDefinitelyNotNullType(original)
else -> null
}
}
fun ConeKotlinType.makeConeTypeDefinitelyNotNullOrNotNull(typeContext: ConeTypeContext): ConeKotlinType {
if (this is ConeIntersectionType) {
return ConeIntersectionType(intersectedTypes.map { it.makeConeTypeDefinitelyNotNullOrNotNull(typeContext) })
}
return ConeDefinitelyNotNullType.create(this, typeContext) ?: this.withNullability(ConeNullability.NOT_NULL, typeContext)
}
fun <T : ConeKotlinType> T.withArguments(arguments: Array<out ConeTypeProjection>, typeSystemContext: ConeTypeContext): T {
if (this.typeArguments === arguments) {
return this
}
@Suppress("UNCHECKED_CAST")
return when (this) {
is ConeClassErrorType -> this
is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(lookupTag, arguments, nullability.isNullable) as T
is ConeDefinitelyNotNullType -> ConeDefinitelyNotNullType(original.withArguments(arguments, typeSystemContext)) as T
else -> error("Not supported: $this: ${this.render()}")
}
}
fun <T : ConeKotlinType> T.withArguments(replacement: (ConeTypeProjection) -> ConeTypeProjection, typeSystemContext: ConeTypeContext) =
withArguments(typeArguments.map(replacement).toTypedArray(), typeSystemContext)
fun <T : ConeKotlinType> T.withAttributes(attributes: ConeAttributes, typeSystemContext: ConeTypeContext): T {
if (this.attributes == attributes) {
return this
}
@Suppress("UNCHECKED_CAST")
return when (this) {
is ConeClassErrorType -> this
is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(lookupTag, typeArguments, nullability.isNullable, attributes)
is ConeDefinitelyNotNullType -> ConeDefinitelyNotNullType(original.withAttributes(attributes, typeSystemContext))
is ConeTypeParameterTypeImpl -> ConeTypeParameterTypeImpl(lookupTag, nullability.isNullable, attributes)
is ConeFlexibleType -> ConeFlexibleType(
lowerBound.withAttributes(attributes, typeSystemContext),
upperBound.withAttributes(attributes, typeSystemContext)
)
is ConeTypeVariableType -> ConeTypeVariableType(nullability, lookupTag, attributes)
is ConeCapturedType -> ConeCapturedType(
captureStatus, lowerType, nullability, constructor, attributes, isProjectionNotNull,
)
// TODO: Consider correct application of attributes to ConeIntersectionType
// Currently, ConeAttributes.union works a bit strange, because it lefts only `other` parts
is ConeIntersectionType -> this
// Attributes for stub types are not supported, and it's not obvious if it should
is ConeStubType -> this
else -> error("Not supported: $this: ${this.render()}")
} as T
}
fun <T : ConeKotlinType> T.withNullability(
nullability: ConeNullability,
typeContext: ConeTypeContext,
attributes: ConeAttributes = this.attributes,
): T {
if (this.nullability == nullability && this.attributes == attributes) {
return this
}
@Suppress("UNCHECKED_CAST")
return when (this) {
is ConeClassErrorType -> this
is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(lookupTag, typeArguments, nullability.isNullable, attributes)
is ConeTypeParameterTypeImpl -> ConeTypeParameterTypeImpl(lookupTag, nullability.isNullable, attributes)
is ConeFlexibleType -> {
if (nullability == ConeNullability.UNKNOWN) {
if (lowerBound.nullability != upperBound.nullability || lowerBound.nullability == ConeNullability.UNKNOWN) {
return this
}
}
coneFlexibleOrSimpleType(
typeContext,
lowerBound.withNullability(nullability, typeContext),
upperBound.withNullability(nullability, typeContext)
)
}
is ConeTypeVariableType -> ConeTypeVariableType(nullability, lookupTag)
is ConeCapturedType -> ConeCapturedType(captureStatus, lowerType, nullability, constructor, attributes)
is ConeIntersectionType -> when (nullability) {
ConeNullability.NULLABLE -> this.mapTypes {
it.withNullability(nullability, typeContext)
}
ConeNullability.UNKNOWN -> this // TODO: is that correct?
ConeNullability.NOT_NULL -> this
}
is ConeStubTypeForBuilderInference -> ConeStubTypeForBuilderInference(variable, nullability)
is ConeStubTypeForTypeVariableInSubtyping -> ConeStubTypeForTypeVariableInSubtyping(variable, nullability)
is ConeDefinitelyNotNullType -> when (nullability) {
ConeNullability.NOT_NULL -> this
ConeNullability.NULLABLE -> original.withNullability(nullability, typeContext)
ConeNullability.UNKNOWN -> original.withNullability(nullability, typeContext)
}
is ConeIntegerLiteralType -> ConeIntegerLiteralTypeImpl(value, isUnsigned, nullability)
else -> error("sealed: ${this::class}")
} as T
}
fun coneFlexibleOrSimpleType(
typeContext: ConeTypeContext,
lowerBound: ConeKotlinType,
upperBound: ConeKotlinType,
): ConeKotlinType {
if (lowerBound is ConeFlexibleType) {
return coneFlexibleOrSimpleType(typeContext, lowerBound.lowerBound, upperBound)
}
if (upperBound is ConeFlexibleType) {
return coneFlexibleOrSimpleType(typeContext, lowerBound, upperBound.upperBound)
}
return when {
AbstractStrictEqualityTypeChecker.strictEqualTypes(typeContext, lowerBound, upperBound) -> lowerBound
else -> ConeFlexibleType(lowerBound, upperBound)
}
}
fun ConeKotlinType.isExtensionFunctionType(session: FirSession): Boolean {
val type = this.lowerBoundIfFlexible().fullyExpandedType(session)
return type.attributes.extensionFunctionType != null
}
fun FirTypeRef.isExtensionFunctionType(session: FirSession): Boolean {
return coneTypeSafe<ConeKotlinType>()?.isExtensionFunctionType(session) == true
}
fun ConeKotlinType.isUnsafeVarianceType(session: FirSession): Boolean {
val type = this.lowerBoundIfFlexible().fullyExpandedType(session)
return type.attributes.unsafeVarianceType != null
}
fun ConeKotlinType.toSymbol(session: FirSession): FirBasedSymbol<*>? {
return (this as? ConeLookupTagBasedType)?.lookupTag?.toSymbol(session)
}
fun ConeKotlinType.toFirResolvedTypeRef(
source: FirSourceElement? = null,
delegatedTypeRef: FirTypeRef? = null
): FirResolvedTypeRef {
return if (this is ConeKotlinErrorType) {
buildErrorTypeRef {
this.source = source
diagnostic = this@toFirResolvedTypeRef.diagnostic
this.delegatedTypeRef = delegatedTypeRef
}
} else {
buildResolvedTypeRef {
this.source = source
type = this@toFirResolvedTypeRef
this.delegatedTypeRef = delegatedTypeRef
}
}
}
fun FirTypeRef.isUnsafeVarianceType(session: FirSession): Boolean {
return coneTypeSafe<ConeKotlinType>()?.isUnsafeVarianceType(session) == true
}
fun FirTypeRef.hasEnhancedNullability(): Boolean =
coneTypeSafe<ConeKotlinType>()?.hasEnhancedNullability == true
fun FirTypeRef.withoutEnhancedNullability(typeSystemContext: ConeTypeContext): FirTypeRef {
require(this is FirResolvedTypeRef)
if (!hasEnhancedNullability()) return this
return buildResolvedTypeRef {
source = this@withoutEnhancedNullability.source
type = this@withoutEnhancedNullability.type.withAttributes(
ConeAttributes.create(
this@withoutEnhancedNullability.type.attributes.filter { it != CompilerConeAttributes.EnhancedNullability }
),
typeSystemContext,
)
annotations += this@withoutEnhancedNullability.annotations
}
}
// Unlike other cases, return types may be implicit, i.e. unresolved
// But in that cases newType should also be `null`
fun FirTypeRef.withReplacedReturnType(newType: ConeKotlinType?): FirTypeRef {
require(this is FirResolvedTypeRef || newType == null)
if (newType == null) return this
return resolvedTypeFromPrototype(newType)
}
fun FirTypeRef.withReplacedConeType(
newType: ConeKotlinType?,
firFakeSourceElementKind: FirFakeSourceElementKind? = null
): FirResolvedTypeRef {
require(this is FirResolvedTypeRef)
if (newType == null) return this
val newSource =
if (firFakeSourceElementKind != null)
this.source?.fakeElement(firFakeSourceElementKind)
else
this.source
return if (newType is ConeKotlinErrorType) {
buildErrorTypeRef {
source = newSource
diagnostic = newType.diagnostic
}
} else {
buildResolvedTypeRef {
source = newSource
type = newType
annotations += this@withReplacedConeType.annotations
delegatedTypeRef = this@withReplacedConeType.delegatedTypeRef
}
}
}
fun FirTypeRef.approximated(
typeApproximator: ConeTypeApproximator,
toSuper: Boolean,
): FirTypeRef {
val alternativeType = (coneType as? ConeIntersectionType)?.alternativeType ?: coneType
if (alternativeType !== coneType && !alternativeType.requiresApproximationInPublicPosition()) {
return withReplacedConeType(alternativeType)
}
val approximatedType = if (toSuper)
typeApproximator.approximateToSuperType(alternativeType, TypeApproximatorConfiguration.PublicDeclaration)
else
typeApproximator.approximateToSubType(alternativeType, TypeApproximatorConfiguration.PublicDeclaration)
return withReplacedConeType(approximatedType)
}
fun FirTypeRef.approximatedIfNeededOrSelf(
approximator: ConeTypeApproximator,
containingCallableVisibility: Visibility?,
typeSystemContext: ConeTypeContext,
isInlineFunction: Boolean = false,
): FirTypeRef {
val approximated = if (containingCallableVisibility == Visibilities.Public || containingCallableVisibility == Visibilities.Protected)
approximatedForPublicPosition(approximator)
else
this
return approximated.hideLocalTypeIfNeeded(containingCallableVisibility, isInlineFunction).withoutEnhancedNullability(typeSystemContext)
}
fun FirTypeRef.approximatedForPublicPosition(approximator: ConeTypeApproximator): FirTypeRef =
if (this is FirResolvedTypeRef && type.requiresApproximationInPublicPosition())
this.approximated(approximator, toSuper = true)
else
this
private fun ConeKotlinType.requiresApproximationInPublicPosition(): Boolean = contains {
it is ConeIntegerLiteralType || it is ConeCapturedType || it is ConeDefinitelyNotNullType || it is ConeIntersectionType
}
/*
* Suppose a function without an explicit return type just returns an anonymous object:
*
* fun foo(...) = object : ObjectSuperType {
* override fun ...
* }
*
* Without unwrapping, the return type ended up with that anonymous object (<no name provided>), while the resolved super type, which
* acts like an implementing interface, is a better fit. In fact, exposing an anonymous object types is prohibited for certain cases,
* e.g., KT-33917. We can also apply this to any local types.
*/
private fun FirTypeRef.hideLocalTypeIfNeeded(
containingCallableVisibility: Visibility?,
isInlineFunction: Boolean = false
): FirTypeRef {
if (!shouldHideLocalType(containingCallableVisibility, isInlineFunction)) return this
val firClass =
(((this as? FirResolvedTypeRef)
?.type as? ConeClassLikeType)
?.lookupTag as? ConeClassLookupTagWithFixedSymbol)
?.symbol?.fir
if (firClass !is FirAnonymousObject) {
// NB: local classes are acceptable here, but reported by EXPOSED_* checkers as errors
return this
}
if (firClass.superTypeRefs.size > 1) {
// NB: don't approximate so members can be resolved. The error is reported by FirAmbiguousAnonymousTypeChecker.
return this
}
val superType = firClass.superTypeRefs.single()
if (superType is FirResolvedTypeRef) {
val newKind = source?.kind
return if (newKind is FirFakeSourceElementKind) superType.copyWithNewSourceKind(newKind) else superType
}
return this
}
fun shouldHideLocalType(containingCallableVisibility: Visibility?, isInlineFunction: Boolean): Boolean {
if (containingCallableVisibility == null) {
return false
}
// Approximate types for non-private (all but package private or private) members.
// Also private inline functions, as per KT-33917.
return containingCallableVisibility == Visibilities.Public ||
containingCallableVisibility == Visibilities.Protected ||
containingCallableVisibility == Visibilities.Internal ||
(containingCallableVisibility == Visibilities.Private && isInlineFunction)
}
fun FirDeclaration.visibilityForApproximation(container: FirDeclaration?): Visibility {
if (this !is FirMemberDeclaration) return Visibilities.Local
val containerVisibility =
if (container == null || container is FirFile) Visibilities.Public
else (container as? FirRegularClass)?.visibility ?: Visibilities.Local
if (containerVisibility == Visibilities.Local || visibility == Visibilities.Local) return Visibilities.Local
if (containerVisibility == Visibilities.Private) return Visibilities.Private
return visibility
}
fun ConeTypeContext.captureFromArgumentsInternal(type: ConeKotlinType, status: CaptureStatus): ConeKotlinType? {
val capturedArguments = captureArguments(type, status) ?: return null
return if (type is ConeFlexibleType) {
ConeFlexibleType(
type.lowerBound.withArguments(capturedArguments, this),
type.upperBound.withArguments(capturedArguments, this),
)
} else {
type.withArguments(capturedArguments, this)
}
}
fun ConeTypeContext.captureArguments(type: ConeKotlinType, status: CaptureStatus): Array<ConeKotlinType>? {
val argumentsCount = type.typeArguments.size
if (argumentsCount == 0) return null
val typeConstructor = type.typeConstructor()
if (argumentsCount != typeConstructor.parametersCount()) return null
if (type.typeArguments.all { it !is ConeStarProjection && it.kind == ProjectionKind.INVARIANT }) return null
val newArguments: Array<ConeKotlinType> = Array(argumentsCount) { index ->
val argument = type.typeArguments[index]
if (argument !is ConeStarProjection && argument.kind == ProjectionKind.INVARIANT)
return@Array argument.type!! // only star projection can return null, but it's guarded above
val lowerType = if (argument !is ConeStarProjection && argument.getVariance() == TypeVariance.IN) {
(argument as ConeKotlinTypeProjection).type
} else {
null
}
ConeCapturedType(status, lowerType, argument, typeConstructor.getParameter(index))
}
val substitution = (0 until argumentsCount).associate { index ->
(typeConstructor.getParameter(index) as ConeTypeParameterLookupTag).symbol to (newArguments[index])
}
val substitutor = substitutorByMap(substitution, session)
for (index in 0 until argumentsCount) {
val oldArgument = type.typeArguments[index]
val newArgument = newArguments[index]
if (oldArgument !is ConeStarProjection && oldArgument.kind == ProjectionKind.INVARIANT) continue
val parameter = typeConstructor.getParameter(index)
val upperBounds = (0 until parameter.upperBoundCount()).mapTo(mutableListOf()) { paramIndex ->
substitutor.safeSubstitute(
this as TypeSystemInferenceExtensionContext, parameter.getUpperBound(paramIndex)
)
}
if (!oldArgument.isStarProjection() && oldArgument.getVariance() == TypeVariance.OUT) {
upperBounds += oldArgument.getType()
}
require(newArgument is ConeCapturedType)
@Suppress("UNCHECKED_CAST")
newArgument.constructor.supertypes = upperBounds as List<ConeKotlinType>
}
return newArguments
}
fun ConeTypeContext.captureFromExpressionInternal(type: ConeKotlinType): ConeKotlinType? {
if (type !is ConeIntersectionType && type !is ConeFlexibleType) {
return captureFromArgumentsInternal(type, CaptureStatus.FROM_EXPRESSION)
}
/*
* We capture arguments in the intersection types in specific way:
* 1) Firstly, we create captured arguments for all type arguments grouped by a type constructor* and a type argument's type.
* It means, that we create only one captured argument for two types `Foo<*>` and `Foo<*>?` within a flexible type, for instance.
* * In addition to grouping by type constructors, we look at possibility locating of two types in different bounds of the same flexible type.
* This is necessary in order to create the same captured arguments,
* for example, for `MutableList` in the lower bound of the flexible type and for `List` in the upper one.
* Example: MutableList<*>..List<*>? -> MutableList<Captured1(*)>..List<Captured2(*)>?, Captured1(*) and Captured2(*) are the same.
* 2) Secondly, we replace type arguments with captured arguments by given a type constructor and type arguments.
*/
val capturedArgumentsByComponents = captureArgumentsForIntersectionType(type) ?: return null
// We reuse `TypeToCapture` for some types, suitability to reuse defines by `isSuitableForType`
fun findCorrespondingCapturedArgumentsForType(type: ConeKotlinType) =
capturedArgumentsByComponents.find { typeToCapture -> typeToCapture.isSuitableForType(type, this) }?.capturedArguments
fun replaceArgumentsWithCapturedArgumentsByIntersectionComponents(typeToReplace: ConeKotlinType): List<ConeKotlinType> {
return if (typeToReplace is ConeIntersectionType) {
typeToReplace.intersectedTypes.map { componentType ->
val capturedArguments = findCorrespondingCapturedArgumentsForType(componentType)
?: return@map componentType
componentType.withArguments(capturedArguments, this)
}
} else {
val capturedArguments = findCorrespondingCapturedArgumentsForType(typeToReplace)
?: return listOf(typeToReplace)
listOf(typeToReplace.withArguments(capturedArguments, this))
}
}
return if (type is ConeFlexibleType) {
val lowerIntersectedType = intersectTypes(replaceArgumentsWithCapturedArgumentsByIntersectionComponents(type.lowerBound))
.withNullability(type.lowerBound.isMarkedNullable) as ConeKotlinType
val upperIntersectedType = intersectTypes(replaceArgumentsWithCapturedArgumentsByIntersectionComponents(type.upperBound))
.withNullability(type.upperBound.isMarkedNullable) as ConeKotlinType
ConeFlexibleType(lowerIntersectedType, upperIntersectedType)
} else {
intersectTypes(replaceArgumentsWithCapturedArgumentsByIntersectionComponents(type)).withNullability(type.isMarkedNullable) as ConeKotlinType
}
}
private fun ConeTypeContext.captureArgumentsForIntersectionType(type: ConeKotlinType): List<CapturedArguments>? {
// It's possible to have one of the bounds as non-intersection type
fun getTypesToCapture(type: ConeKotlinType) =
if (type is ConeIntersectionType) type.intersectedTypes else listOf(type)
val filteredTypesToCapture =
when (type) {
is ConeFlexibleType -> {
val typesToCapture = getTypesToCapture(type.lowerBound) + getTypesToCapture(type.upperBound)
typesToCapture.distinctBy {
(ConeFlexibleTypeBoundsChecker.getBaseBoundFqNameByMutability(it) ?: it.typeConstructor(this)) to it.typeArguments
}
}
is ConeIntersectionType -> type.intersectedTypes
else -> error("Should not be here")
}
var changed = false
val capturedArgumentsByTypes = filteredTypesToCapture.mapNotNull { typeToCapture ->
val capturedArguments = captureArguments(typeToCapture, CaptureStatus.FROM_EXPRESSION)
?: return@mapNotNull null
changed = true
CapturedArguments(capturedArguments, originalType = typeToCapture)
}
if (!changed) return null
return capturedArgumentsByTypes
}
private class CapturedArguments(val capturedArguments: Array<out ConeTypeProjection>, private val originalType: ConeKotlinType) {
fun isSuitableForType(type: ConeKotlinType, context: ConeTypeContext): Boolean {
val areArgumentsMatched = type.typeArguments.withIndex().all { (i, typeArgumentsType) ->
originalType.typeArguments.size > i && typeArgumentsType == originalType.typeArguments[i]
}
if (!areArgumentsMatched) return false
val areConstructorsMatched = originalType.typeConstructor(context) == type.typeConstructor(context)
|| ConeFlexibleTypeBoundsChecker.areTypesMayBeLowerAndUpperBoundsOfSameFlexibleTypeByMutability(originalType, type)
if (!areConstructorsMatched) return false
return true
}
}
fun ConeKotlinType.isSubtypeOf(superType: ConeKotlinType, session: FirSession): Boolean =
AbstractTypeChecker.isSubtypeOf(
session.typeContext.newTypeCheckerState(errorTypesEqualToAnything = false, stubTypesEqualToAnything = false),
this, superType,
)
fun FirTypedDeclaration.isSubtypeOf(
other: FirTypedDeclaration,
typeCheckerContext: TypeCheckerState
): Boolean {
return AbstractTypeChecker.isSubtypeOf(
typeCheckerContext,
returnTypeRef.coneType,
other.returnTypeRef.coneType
)
}