[FIR] Store expressions of receivers inside candidates instead of ReceiverValue

This is needed because of mutable nature of receiver values: implicit
  receiver values can be modified because of smartcasts, but in candidate
  we need to store snapshot of receiver in the form it was at the beginning
  of the resolution

^KT-58823 Fixed
This commit is contained in:
Dmitriy Novozhilov
2023-06-05 10:36:55 +03:00
committed by Space Team
parent b564260a33
commit 6409bf2fe8
26 changed files with 255 additions and 181 deletions
@@ -447,15 +447,15 @@ internal class KtFirCallResolver(
) {
// Implicit invoke (e.g., `x()`) will have a different callee symbol (e.g., `x`) than the candidate (e.g., `invoke`).
createKtPartiallyAppliedSymbolForImplicitInvoke(
candidate.dispatchReceiverValue?.receiverExpression ?: FirNoReceiverExpression,
candidate.chosenExtensionReceiverValue?.receiverExpression ?: FirNoReceiverExpression,
candidate.dispatchReceiver ?: FirNoReceiverExpression,
candidate.chosenExtensionReceiver ?: FirNoReceiverExpression,
candidate.explicitReceiverKind
)
} else {
KtPartiallyAppliedSymbol(
with(analysisSession) { unsubstitutedKtSignature.substitute(substitutor) },
candidate.dispatchReceiverValue?.receiverExpression?.toKtReceiverValue(),
candidate.chosenExtensionReceiverValue?.receiverExpression?.toKtReceiverValue(),
candidate.dispatchReceiver?.toKtReceiverValue(),
candidate.chosenExtensionReceiver?.toKtReceiverValue(),
)
}
} else if (fir is FirImplicitInvokeCall) {
@@ -57,9 +57,7 @@ internal class KtFirVisibilityChecker(
val dispatchReceiverCanBeExplicit = candidateSymbol is KtCallableSymbol && !candidateSymbol.isExtension
val explicitDispatchReceiver = runIf(dispatchReceiverCanBeExplicit) {
receiverExpression
?.getOrBuildFirSafe<FirExpression>(analysisSession.firResolveSession)
?.let { ExpressionReceiverValue(it) }
receiverExpression?.getOrBuildFirSafe<FirExpression>(analysisSession.firResolveSession)
}
val candidateFirSymbol = candidateSymbol.firSymbol.fir as FirMemberDeclaration
@@ -53,12 +53,12 @@ class SingleCandidateResolver(
callInfo,
resolutionParameters.callableSymbol,
explicitReceiverKind = explicitReceiverKind,
dispatchReceiverValue = dispatchReceiverValue,
dispatchReceiver = dispatchReceiverValue?.receiverExpression,
givenExtensionReceiverOptions = listOfNotNull(
if (explicitReceiverKind.isExtensionReceiver)
callInfo.explicitReceiver?.let { ExpressionReceiverValue(it) }
callInfo.explicitReceiver
else
implicitExtensionReceiverValue
implicitExtensionReceiverValue?.receiverExpression
),
scope = null,
)
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.originalForSubstitutionOverride
import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.resolve.calls.ExpressionReceiverValue
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDiagnosticWithCandidates
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
@@ -51,7 +50,7 @@ object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker()
context.session,
context.findClosest()!!,
context.containingDeclarations,
ExpressionReceiverValue(expression.dispatchReceiver),
expression.dispatchReceiver,
)
}
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticPropertyAccessor
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.resolve.SupertypeSupplier
import org.jetbrains.kotlin.fir.resolve.calls.FirSimpleSyntheticPropertySymbol
import org.jetbrains.kotlin.fir.resolve.calls.ReceiverValue
@@ -30,7 +31,7 @@ object FirJavaVisibilityChecker : FirVisibilityChecker() {
symbol: FirBasedSymbol<*>,
useSiteFile: FirFile,
containingDeclarations: List<FirDeclaration>,
dispatchReceiver: ReceiverValue?,
dispatchReceiver: FirExpression?,
session: FirSession,
isCallToPropertySetter: Boolean,
supertypeSupplier: SupertypeSupplier
@@ -12,13 +12,12 @@ 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.declarations.utils.isStatic
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
import org.jetbrains.kotlin.fir.expressions.toReference
import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.ExpressionReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ReceiverValue
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.resolve.providers.getContainingFile
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
@@ -54,7 +53,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
symbol: FirBasedSymbol<*>,
useSiteFile: FirFile,
containingDeclarations: List<FirDeclaration>,
dispatchReceiver: ReceiverValue?,
dispatchReceiver: FirExpression?,
session: FirSession,
isCallToPropertySetter: Boolean,
supertypeSupplier: SupertypeSupplier
@@ -95,7 +94,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
session: FirSession,
useSiteFile: FirFile,
containingDeclarations: List<FirDeclaration>,
dispatchReceiver: ReceiverValue?,
dispatchReceiver: FirExpression?,
isCallToPropertySetter: Boolean = false,
staticQualifierClassForCallable: FirRegularClass? = null,
// There's no need to check if containing class is visible in case we check if a member might be overridden in a subclass
@@ -172,16 +171,16 @@ abstract class FirVisibilityChecker : FirSessionComponent {
private fun FirMemberDeclaration.containingNonLocalClass(
session: FirSession,
dispatchReceiverValue: ReceiverValue?,
dispatchReceiver: FirExpression?,
containingUseSiteDeclarations: List<FirDeclaration>,
supertypeSupplier: SupertypeSupplier
): FirClassLikeDeclaration? {
return when (this) {
is FirCallableDeclaration -> {
if (dispatchReceiverValue != null) {
if (dispatchReceiver != null) {
val baseReceiverType = dispatchReceiverClassTypeOrNull()
if (baseReceiverType != null) {
dispatchReceiverValue.type.findClassRepresentation(baseReceiverType, session)?.toSymbol(session)?.fir?.let {
dispatchReceiver.typeRef.coneType.findClassRepresentation(baseReceiverType, session)?.toSymbol(session)?.fir?.let {
return it
}
}
@@ -220,7 +219,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
session: FirSession,
useSiteFile: FirFile,
containingDeclarations: List<FirDeclaration>,
dispatchReceiver: ReceiverValue?,
dispatchReceiver: FirExpression?,
isCallToPropertySetter: Boolean = false,
supertypeSupplier: SupertypeSupplier
): Boolean {
@@ -288,7 +287,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
symbol: FirBasedSymbol<*>,
useSiteFile: FirFile,
containingDeclarations: List<FirDeclaration>,
dispatchReceiver: ReceiverValue?,
dispatchReceiver: FirExpression?,
session: FirSession,
isCallToPropertySetter: Boolean,
supertypeSupplier: SupertypeSupplier
@@ -304,7 +303,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
symbol: FirBasedSymbol<*>,
containingDeclarationOfUseSite: List<FirDeclaration>,
ownerLookupTag: ConeClassLikeLookupTag,
dispatchReceiver: ReceiverValue?,
dispatchReceiver: FirExpression?,
isVariableOrNamedFunction: Boolean,
session: FirSession
): Boolean {
@@ -332,7 +331,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
val dispatchReceiverParameterClassLookupTag = dispatchReceiverParameterClassSymbol.toLookupTag()
val dispatchReceiverValueOwnerLookupTag =
dispatchReceiver.type.findClassRepresentation(
dispatchReceiver.typeRef.coneType.findClassRepresentation(
dispatchReceiverParameterClassLookupTag.constructClassType(
Array(dispatchReceiverParameterClassSymbol.fir.typeParameters.size) { ConeStarProjection },
isNullable = true
@@ -342,21 +341,13 @@ abstract class FirVisibilityChecker : FirSessionComponent {
if (dispatchReceiverParameterClassLookupTag != dispatchReceiverValueOwnerLookupTag) return false
if (fir.visibility == Visibilities.PrivateToThis) {
when (dispatchReceiver) {
is ExpressionReceiverValue -> {
val explicitReceiver = dispatchReceiver.explicitReceiver
if (explicitReceiver !is FirThisReceiverExpression) {
return false
}
if (explicitReceiver.calleeReference.boundSymbol != dispatchReceiverParameterClassSymbol) {
return false
}
}
is ImplicitReceiverValue<*> -> {
if (dispatchReceiver.boundSymbol != dispatchReceiverParameterClassSymbol) {
when (val dispatchReceiverReference = dispatchReceiver.toReference()) {
is FirThisReference -> {
if (dispatchReceiverReference.boundSymbol != dispatchReceiverParameterClassSymbol) {
return false
}
}
else -> return false
}
}
}
@@ -385,7 +376,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
private fun canSeeProtectedMemberOf(
containingUseSiteClass: FirClass,
dispatchReceiver: ReceiverValue?,
dispatchReceiver: FirExpression?,
ownerLookupTag: ConeClassLikeLookupTag,
session: FirSession,
isVariableOrNamedFunction: Boolean,
@@ -410,20 +401,17 @@ abstract class FirVisibilityChecker : FirSessionComponent {
}
private fun doesReceiverFitForProtectedVisibility(
dispatchReceiver: ReceiverValue?,
dispatchReceiver: FirExpression?,
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
}
var dispatchReceiverType = dispatchReceiver.typeRef.coneType
if (dispatchReceiver is FirPropertyAccessExpression && dispatchReceiver.calleeReference is FirSuperReference) {
// Special 'super' case: type of this, not of super, should be taken for the check below
dispatchReceiverType = dispatchReceiver.dispatchReceiver.typeRef.coneType
}
val typeCheckerState = session.typeContext.newTypeCheckerState(
errorTypesEqualToAnything = false,
@@ -448,8 +436,9 @@ abstract class FirVisibilityChecker : FirSessionComponent {
return false
}
private fun ReceiverValue?.ownerIfCompanion(session: FirSession): ConeClassLikeLookupTag? =
(this?.type as? ConeClassLikeType)?.lookupTag?.ownerIfCompanion(session)
private fun FirExpression?.ownerIfCompanion(session: FirSession): ConeClassLikeLookupTag? =
// TODO: what if there is an intersection type from smartcast?
(this?.typeRef?.coneType 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.
@@ -464,7 +453,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
protected fun canSeeProtectedMemberOf(
usedSymbol: FirBasedSymbol<*>,
containingDeclarationOfUseSite: List<FirDeclaration>,
dispatchReceiver: ReceiverValue?,
dispatchReceiver: FirExpression?,
ownerLookupTag: ConeClassLikeLookupTag,
session: FirSession,
isVariableOrNamedFunction: Boolean,
@@ -36,9 +36,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.SmartcastStability
import org.jetbrains.kotlin.utils.addToStdlib.applyIf
interface Receiver
interface ReceiverValue : Receiver {
interface ReceiverValue {
val type: ConeKotlinType
val receiverExpression: FirExpression
@@ -51,8 +49,7 @@ interface ReceiverValue : Receiver {
)
}
// TODO: should inherit just Receiver, not ReceiverValue
abstract class AbstractExplicitReceiver<E : FirExpression> : Receiver {
abstract class AbstractExplicitReceiver<E : FirExpression> {
abstract val explicitReceiver: FirExpression
}
@@ -62,11 +59,11 @@ abstract class AbstractExplicitReceiverValue<E : FirExpression> : AbstractExplic
get() = explicitReceiver.typeRef.coneTypeSafe()
?: ConeErrorType(ConeIntermediateDiagnostic("No type calculated for: ${explicitReceiver.renderWithType()}")) // TODO: assert here
override val receiverExpression: FirExpression
final override val receiverExpression: FirExpression
get() = explicitReceiver
}
open class ExpressionReceiverValue(
class ExpressionReceiverValue(
override val explicitReceiver: FirExpression
) : AbstractExplicitReceiverValue<FirExpression>(), ReceiverValue {
override fun scope(useSiteSession: FirSession, scopeSession: ScopeSession): FirTypeScope? {
@@ -121,10 +118,46 @@ sealed class ImplicitReceiverValue<S : FirBasedSymbol<*>>(
override fun scope(useSiteSession: FirSession, scopeSession: ScopeSession): FirTypeScope? = implicitScope
private val originalReceiverExpression: FirExpression =
private var receiverIsSmartcasted: Boolean = false
private var originalReceiverExpression: FirExpression =
receiverExpression(boundSymbol, type, contextReceiverNumber, inaccessibleReceiver)
final override var receiverExpression: FirExpression = originalReceiverExpression
private set
private var _receiverExpression: FirExpression? = null
private fun computeReceiverExpression(): FirExpression {
_receiverExpression?.let { return it }
val actualReceiverExpression = if (receiverIsSmartcasted) {
buildSmartCastExpression {
originalExpression = originalReceiverExpression
this.source = originalExpression.source?.fakeElement(KtFakeSourceElementKind.SmartCastExpression)
smartcastType = buildResolvedTypeRef {
source = originalReceiverExpression.typeRef.source?.fakeElement(KtFakeSourceElementKind.SmartCastedTypeRef)
type = this@ImplicitReceiverValue.type
}
typesFromSmartCast = listOf(type)
smartcastStability = SmartcastStability.STABLE_VALUE
typeRef = smartcastType.copyWithNewSourceKind(KtFakeSourceElementKind.ImplicitTypeRef)
}
} else {
originalReceiverExpression
}
_receiverExpression = actualReceiverExpression
return actualReceiverExpression
}
/**
* The idea of receiver expression for implicit receivers is following:
* - Implicit receivers are mutable because of smartcasts
* - Expression of implicit receiver may be used during call resolution and then stored for later. This implies necesserity
* to keep receiver expression independent of state of corresponding implicit value
* - In the same time we don't want to create new receiver expression for each access in sake of performance
* All those statements lead to the current implementation:
* - original receiver expression (without smartcast) always stored inside receiver value and can not be changed (TODO: except builder inference)
* - we keep information about was there smartcast or not in [receiverIsSmartcasted] field
* - we cache computed receiver expression in [_receiverExpression] field
* - if type of receiver value was changed this cache is dropped
*/
final override val receiverExpression: FirExpression
get() = computeReceiverExpression()
@RequiresOptIn
annotation class ImplicitReceiverInternals
@@ -132,7 +165,8 @@ sealed class ImplicitReceiverValue<S : FirBasedSymbol<*>>(
@Deprecated(level = DeprecationLevel.ERROR, message = "Builder inference should not modify implicit receivers. KT-54708")
fun updateTypeInBuilderInference(type: ConeKotlinType) {
this.type = type
receiverExpression = receiverExpression(boundSymbol, type, contextReceiverNumber, inaccessibleReceiver)
originalReceiverExpression = receiverExpression(boundSymbol, type, contextReceiverNumber, inaccessibleReceiver)
_receiverExpression = null
implicitScope = type.scope(
useSiteSession = useSiteSession,
scopeSession = scopeSession,
@@ -147,24 +181,10 @@ sealed class ImplicitReceiverValue<S : FirBasedSymbol<*>>(
@ImplicitReceiverInternals
fun updateTypeFromSmartcast(type: ConeKotlinType) {
if (type == this.type) return
if (!mutable) throw IllegalStateException("Cannot mutate an immutable ImplicitReceiverValue")
if (!mutable) error("Cannot mutate an immutable ImplicitReceiverValue")
this.type = type
receiverExpression = if (type == originalReceiverExpression.typeRef.coneType) {
originalReceiverExpression
} else {
buildSmartCastExpression {
originalExpression = originalReceiverExpression
this.source = originalExpression.source?.fakeElement(KtFakeSourceElementKind.SmartCastExpression)
smartcastType = buildResolvedTypeRef {
source = originalReceiverExpression.typeRef.source?.fakeElement(KtFakeSourceElementKind.SmartCastedTypeRef)
this.type = type
}
typesFromSmartCast = listOf(type)
smartcastStability = SmartcastStability.STABLE_VALUE
typeRef = smartcastType.copyWithNewSourceKind(KtFakeSourceElementKind.ImplicitTypeRef)
}
}
receiverIsSmartcasted = type != this.originalType
_receiverExpression = null
implicitScope = type.scope(
useSiteSession = useSiteSession,
scopeSession = scopeSession,
@@ -30,10 +30,10 @@ class Candidate(
// - in case a use-site receiver is explicit
// - in some cases with static entities, no matter is a use-site receiver explicit or not
// OR we may have here a kind of ImplicitReceiverValue (non-statics only)
override var dispatchReceiverValue: ReceiverValue?,
override var dispatchReceiver: FirExpression?,
// In most cases, it contains zero or single element
// More than one, only in case of context receiver group
val givenExtensionReceiverOptions: List<ReceiverValue>,
val givenExtensionReceiverOptions: List<FirExpression>,
override val explicitReceiverKind: ExplicitReceiverKind,
private val constraintSystemFactory: InferenceComponents.ConstraintSystemFactory,
private val baseSystem: ConstraintStorage,
@@ -44,7 +44,7 @@ class Candidate(
val isFromOriginalTypeInPresenceOfSmartCast: Boolean = false,
) : AbstractCandidate() {
var systemInitialized: Boolean = false
private var systemInitialized: Boolean = false
val system: NewConstraintSystemImpl by lazy(LazyThreadSafetyMode.NONE) {
val system = constraintSystemFactory.createConstraintSystem()
system.addOtherSystem(baseSystem)
@@ -80,7 +80,7 @@ class Candidate(
var currentApplicability = CandidateApplicability.RESOLVED
private set
override var chosenExtensionReceiverValue: ReceiverValue? = givenExtensionReceiverOptions.singleOrNull()
override var chosenExtensionReceiver: FirExpression? = givenExtensionReceiverOptions.singleOrNull()
var contextReceiverArguments: List<FirExpression>? = null
@@ -103,11 +103,13 @@ class Candidate(
var passedStages: Int = 0
// FirExpressionStub can be located here in case of callable reference resolution
fun dispatchReceiverExpression(): FirExpression =
dispatchReceiverValue?.receiverExpression?.takeIf { it !is FirExpressionStub } ?: FirNoReceiverExpression
dispatchReceiver?.takeIf { it !is FirExpressionStub } ?: FirNoReceiverExpression
// FirExpressionStub can be located here in case of callable reference resolution
fun chosenExtensionReceiverExpression(): FirExpression =
chosenExtensionReceiverValue?.receiverExpression?.takeIf { it !is FirExpressionStub } ?: FirNoReceiverExpression
chosenExtensionReceiver?.takeIf { it !is FirExpressionStub } ?: FirNoReceiverExpression
fun contextReceiverArguments(): List<FirExpression> =
contextReceiverArguments ?: emptyList()
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.originalForWrappedIntegerOperator
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzerContext
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
@@ -54,8 +55,8 @@ class CandidateFactory private constructor(
symbol: FirBasedSymbol<*>,
explicitReceiverKind: ExplicitReceiverKind,
scope: FirScope?,
dispatchReceiverValue: ReceiverValue? = null,
givenExtensionReceiverOptions: List<ReceiverValue> = emptyList(),
dispatchReceiver: FirExpression? = null,
givenExtensionReceiverOptions: List<FirExpression> = emptyList(),
objectsByName: Boolean = false,
isFromOriginalTypeInPresenceOfSmartCast: Boolean = false,
): Candidate {
@@ -64,7 +65,7 @@ class CandidateFactory private constructor(
val result = Candidate(
symbol,
dispatchReceiverValue,
dispatchReceiver,
givenExtensionReceiverOptions,
explicitReceiverKind,
context.inferenceComponents.constraintSystemFactory,
@@ -74,7 +75,7 @@ class CandidateFactory private constructor(
isFromCompanionObjectTypeScope = when (explicitReceiverKind) {
ExplicitReceiverKind.EXTENSION_RECEIVER ->
givenExtensionReceiverOptions.singleOrNull().isCandidateFromCompanionObjectTypeScope()
ExplicitReceiverKind.DISPATCH_RECEIVER -> dispatchReceiverValue.isCandidateFromCompanionObjectTypeScope()
ExplicitReceiverKind.DISPATCH_RECEIVER -> dispatchReceiver.isCandidateFromCompanionObjectTypeScope()
// The following cases are not applicable for companion objects.
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> false
},
@@ -117,8 +118,12 @@ class CandidateFactory private constructor(
private fun ReceiverValue?.isCandidateFromCompanionObjectTypeScope(): Boolean {
val expressionReceiverValue = this as? ExpressionReceiverValue ?: return false
val resolvedQualifier = (expressionReceiverValue.explicitReceiver as? FirResolvedQualifier) ?: return false
val originClassOfCandidate = expressionReceiverValue.type.classId ?: return false
return expressionReceiverValue.explicitReceiver.isCandidateFromCompanionObjectTypeScope()
}
private fun FirExpression?.isCandidateFromCompanionObjectTypeScope(): Boolean {
val resolvedQualifier = this as? FirResolvedQualifier ?: return false
val originClassOfCandidate = this.typeRef.coneType.classId ?: return false
return (resolvedQualifier.symbol?.fir as? FirRegularClass)?.companionObjectSymbol?.classId == originClassOfCandidate
}
@@ -134,7 +139,7 @@ class CandidateFactory private constructor(
}
return Candidate(
symbol,
dispatchReceiverValue = null,
dispatchReceiver = null,
givenExtensionReceiverOptions = emptyList(),
explicitReceiverKind = ExplicitReceiverKind.NO_EXPLICIT_RECEIVER,
context.inferenceComponents.constraintSystemFactory,
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.matchingParameterFunctionType
import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.resolve.directExpansionType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.inference.ConeTypeParameterBasedTypeVariable
@@ -25,10 +26,8 @@ import org.jetbrains.kotlin.fir.resolve.inference.csBuilder
import org.jetbrains.kotlin.fir.resolve.inference.hasBuilderInferenceAnnotation
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeExplicitTypeParameterConstraintPosition
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.FirUnstableSmartcastTypeScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.SyntheticSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
@@ -135,17 +134,17 @@ object CheckExtensionReceiver : ResolutionStage() {
}
private fun Candidate.prepareReceivers(
argumentExtensionReceiverValue: ReceiverValue,
argumentExtensionReceiver: FirExpression,
expectedType: ConeKotlinType,
context: ResolutionContext,
): ReceiverDescription {
val argumentType = captureFromTypeParameterUpperBoundIfNeeded(
argumentType = argumentExtensionReceiverValue.type,
argumentType = argumentExtensionReceiver.typeRef.coneType,
expectedType = expectedType,
session = context.session
).let { prepareCapturedType(it, context) }
return ReceiverDescription(argumentExtensionReceiverValue.receiverExpression, argumentType)
return ReceiverDescription(argumentExtensionReceiver, argumentType)
}
private class ReceiverDescription(
@@ -163,7 +162,7 @@ object CheckDispatchReceiver : ResolutionStage() {
}
}
val dispatchReceiverValueType = candidate.dispatchReceiverValue?.type ?: return
val dispatchReceiverValueType = candidate.dispatchReceiver?.typeRef?.coneType ?: return
val isReceiverNullable = !AbstractNullabilityChecker.isSubtypeOfAny(context.session.typeContext, dispatchReceiverValueType)
val isCandidateFromUnstableSmartcast =
@@ -205,9 +204,10 @@ object CheckContextReceivers : ResolutionStage() {
candidate.substitutor.substituteOrSelf(it.typeRef.coneType)
}?.takeUnless { it.isEmpty() } ?: return
val receiverGroups: List<List<ImplicitReceiverValue<*>>> =
val receiverGroups: List<List<FirExpression>> =
context.bodyResolveContext.towerDataContext.towerDataElements.asReversed().mapNotNull { towerDataElement ->
towerDataElement.implicitReceiver?.let(::listOf) ?: towerDataElement.contextReceiverGroup
towerDataElement.implicitReceiver?.receiverExpression?.let(::listOf)
?: towerDataElement.contextReceiverGroup?.map { it.receiverExpression }
}
val resultingContextReceiverArguments = mutableListOf<FirExpression>()
@@ -236,7 +236,7 @@ object CheckContextReceivers : ResolutionStage() {
private fun Candidate.findClosestMatchingReceivers(
expectedType: ConeKotlinType,
receiverGroups: List<List<ImplicitReceiverValue<*>>>,
receiverGroups: List<List<FirExpression>>,
context: ResolutionContext,
): List<ReceiverDescription> {
for (receiverGroup in receiverGroups) {
@@ -259,18 +259,24 @@ object CheckDslScopeViolation : ResolutionStage() {
private val dslMarkerClassId = ClassId.fromString("kotlin/DslMarker")
override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
fun checkReceiverValue(receiverValue: ReceiverValue?) {
if (receiverValue is ImplicitReceiverValue<*>) {
receiverValue.checkImpl(
fun checkReceiver(receiver: FirExpression?) {
val thisReference = receiver?.toReference() as? FirThisReference ?: return
if (thisReference.isImplicit) {
checkImpl(
candidate,
sink,
context,
{ receiverValue.getDslMarkersOfImplicitReceiver(context) }
) { a, b -> a == b }
{ getDslMarkersOfImplicitReceiver(thisReference.boundSymbol, receiver.typeRef.coneType, context) }
) {
// TODO: is there better alternative?
// Here we rely on the fact that receiver expression of implicit receiver value can not be changed
// during resolution of one single call
it.receiverExpression == receiver
}
}
}
checkReceiverValue(candidate.dispatchReceiverValue)
checkReceiverValue(candidate.chosenExtensionReceiverValue)
checkReceiver(candidate.dispatchReceiver)
checkReceiver(candidate.chosenExtensionReceiver)
// For value of builtin functional type with implicit extension receiver, the receiver is passed as the first argument rather than
// an extension receiver of the `invoke` call. Hence, we need to specially handle this case.
@@ -299,31 +305,31 @@ object CheckDslScopeViolation : ResolutionStage() {
// ```
// `useX()` is a call to `invoke` with `useX` as the dispatch receiver. In the FIR tree, extension receiver is represented as an
// implicit `this` expression passed as the first argument.
if (candidate.dispatchReceiverValue?.type?.fullyExpandedType(context.session)?.isSomeFunctionType(context.session) == true &&
if (candidate.dispatchReceiver?.typeRef?.coneType?.fullyExpandedType(context.session)?.isSomeFunctionType(context.session) == true &&
(candidate.symbol as? FirNamedFunctionSymbol)?.name == OperatorNameConventions.INVOKE
) {
val firstArg = candidate.argumentMapping?.keys?.firstOrNull() as? FirThisReceiverExpression ?: return
if (!firstArg.isImplicit) return
firstArg.checkImpl(
checkImpl(
candidate,
sink,
context,
{ firstArg.getDslMarkersOfThisReceiverExpression(context) }
) { receiver, thisExpression -> receiver.boundSymbol == thisExpression.calleeReference.boundSymbol }
) { it.boundSymbol == firstArg.calleeReference.boundSymbol }
}
}
/**
* Checks whether the implicit receiver (represented as an object of type `T`) violates DSL scope rules.
*/
private fun <T> T.checkImpl(
private fun checkImpl(
candidate: Candidate,
sink: CheckerSink,
context: ResolutionContext,
dslMarkersProvider: () -> Set<ClassId>,
isImplicitReceiverMatching: (ImplicitReceiverValue<*>, T) -> Boolean,
isImplicitReceiverMatching: (ImplicitReceiverValue<*>) -> Boolean,
) {
val resolvedReceiverIndex = context.bodyResolveContext.implicitReceiverStack.indexOfFirst { isImplicitReceiverMatching(it, this) }
val resolvedReceiverIndex = context.bodyResolveContext.implicitReceiverStack.indexOfFirst { isImplicitReceiverMatching(it) }
if (resolvedReceiverIndex == -1) return
val closerReceivers = context.bodyResolveContext.implicitReceiverStack.drop(resolvedReceiverIndex + 1)
if (closerReceivers.isEmpty()) return
@@ -335,6 +341,14 @@ object CheckDslScopeViolation : ResolutionStage() {
}
private fun ImplicitReceiverValue<*>.getDslMarkersOfImplicitReceiver(context: ResolutionContext): Set<ClassId> {
return CheckDslScopeViolation.getDslMarkersOfImplicitReceiver(boundSymbol, type, context)
}
private fun getDslMarkersOfImplicitReceiver(
boundSymbol: FirBasedSymbol<*>?,
type: ConeKotlinType,
context: ResolutionContext,
): Set<ClassId> {
return buildSet {
(boundSymbol as? FirAnonymousFunctionSymbol)?.fir?.matchingParameterFunctionType?.let {
// collect annotations in the function type at declaration site. For example, the `@A` and `@B` in the following code.
@@ -550,7 +564,7 @@ internal object CheckVisibility : CheckerStage() {
val visible = visibilityChecker.isVisible(
declaration,
candidate.callInfo,
dispatchReceiverValue = null
dispatchReceiver = null
)
if (!visible) {
sink.yieldDiagnostic(VisibilityError)
@@ -696,8 +710,8 @@ private val DYNAMIC_EXTENSION_ANNOTATION_CLASS_ID: ClassId = ClassId.topLevel(DY
internal object ProcessDynamicExtensionAnnotation : ResolutionStage() {
override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
if (candidate.symbol.origin === FirDeclarationOrigin.DynamicScope) return
val extensionReceiver = candidate.chosenExtensionReceiverValue ?: return
val argumentIsDynamic = extensionReceiver.type is ConeDynamicType
val extensionReceiver = candidate.chosenExtensionReceiver ?: return
val argumentIsDynamic = extensionReceiver.typeRef.coneType is ConeDynamicType
val parameterIsDynamic = (candidate.symbol as? FirCallableSymbol)?.resolvedReceiverTypeRef?.type is ConeDynamicType
if (parameterIsDynamic != argumentIsDynamic ||
parameterIsDynamic && !candidate.symbol.hasAnnotation(DYNAMIC_EXTENSION_ANNOTATION_CLASS_ID, context.session)
@@ -13,10 +13,9 @@ import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.getExplicitBackingField
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildSmartCastExpression
import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
@@ -30,14 +29,18 @@ import org.jetbrains.kotlin.utils.addToStdlib.runIf
fun FirVisibilityChecker.isVisible(
declaration: FirMemberDeclaration,
callInfo: CallInfo,
dispatchReceiverValue: ReceiverValue?
dispatchReceiver: FirExpression?
): Boolean {
val staticQualifierForCallable = runIf(declaration is FirCallableDeclaration && declaration.isStatic) {
val explicitReceiver = (dispatchReceiverValue as? ExpressionReceiverValue)?.explicitReceiver
when (val classLikeSymbol = (explicitReceiver as? FirResolvedQualifier)?.symbol) {
val staticQualifierForCallable = runIf(
declaration is FirCallableDeclaration &&
declaration.isStatic &&
isExplicitReceiverExpression(dispatchReceiver)
) {
when (val classLikeSymbol = (dispatchReceiver as? FirResolvedQualifier)?.symbol) {
is FirRegularClassSymbol -> classLikeSymbol.fir
is FirTypeAliasSymbol -> classLikeSymbol.fullyExpandedClass(callInfo.session)?.fir
is FirAnonymousObjectSymbol, null -> null
is FirAnonymousObjectSymbol,
null -> null
}
}
return isVisible(
@@ -45,7 +48,7 @@ fun FirVisibilityChecker.isVisible(
callInfo.session,
callInfo.containingFile,
callInfo.containingDeclarations,
dispatchReceiverValue,
dispatchReceiver,
staticQualifierClassForCallable = staticQualifierForCallable,
isCallToPropertySetter = callInfo.callSite is FirVariableAssignment
)
@@ -57,28 +60,29 @@ fun FirVisibilityChecker.isVisible(
): Boolean {
val callInfo = candidate.callInfo
if (!isVisible(declaration, callInfo, candidate.dispatchReceiverValue)) {
if (!isVisible(declaration, callInfo, candidate.dispatchReceiver)) {
val dispatchReceiverWithoutSmartCastType =
removeSmartCastTypeForAttemptToFitVisibility(candidate.dispatchReceiverValue, candidate.callInfo.session) ?: return false
removeSmartCastTypeForAttemptToFitVisibility(candidate.dispatchReceiver, candidate.callInfo.session) ?: return false
if (!isVisible(declaration, callInfo, dispatchReceiverWithoutSmartCastType)) return false
candidate.dispatchReceiverValue = dispatchReceiverWithoutSmartCastType
candidate.dispatchReceiver = dispatchReceiverWithoutSmartCastType
}
val backingField = declaration.getBackingFieldIfApplicable()
if (backingField != null) {
candidate.hasVisibleBackingField = isVisible(backingField, callInfo, candidate.dispatchReceiverValue)
candidate.hasVisibleBackingField = isVisible(backingField, callInfo, candidate.dispatchReceiver)
}
return true
}
private fun removeSmartCastTypeForAttemptToFitVisibility(dispatchReceiverValue: ReceiverValue?, session: FirSession): ReceiverValue? {
private fun removeSmartCastTypeForAttemptToFitVisibility(dispatchReceiver: FirExpression?, session: FirSession): FirExpression? {
val expressionWithSmartcastIfStable =
(dispatchReceiverValue?.receiverExpression as? FirSmartCastExpression)?.takeIf { it.isStable } ?: return null
(dispatchReceiver as? FirSmartCastExpression)?.takeIf { it.isStable } ?: return null
if (dispatchReceiverValue.type.isNullableNothing) return null
val receiverType = dispatchReceiver.typeRef.coneType
if (receiverType.isNullableNothing) return null
val originalExpression = expressionWithSmartcastIfStable.originalExpression
val originalType = originalExpression.typeRef.coneType
@@ -87,11 +91,11 @@ private fun removeSmartCastTypeForAttemptToFitVisibility(dispatchReceiverValue:
// Basically, this `if` is just for sake of optimizaton
// We have only nullability enhancement, here, so return initial smart cast receiver value
if (originalTypeNotNullable == dispatchReceiverValue.type) return null
if (originalTypeNotNullable == receiverType) return null
val expressionForReceiver = with(session.typeContext) {
when {
originalType.isNullableType() && !dispatchReceiverValue.type.isNullableType() ->
originalType.isNullableType() && !receiverType.isNullableType() ->
buildSmartCastExpression {
source = originalExpression.source?.fakeElement(KtFakeSourceElementKind.SmartCastExpression)
this.originalExpression = originalExpression
@@ -107,7 +111,7 @@ private fun removeSmartCastTypeForAttemptToFitVisibility(dispatchReceiverValue:
}
}
return ExpressionReceiverValue(expressionForReceiver)
return expressionForReceiver
}
@@ -123,3 +127,10 @@ private fun FirMemberDeclaration.getBackingFieldIfApplicable(): FirBackingField?
else -> null
}
}
private fun isExplicitReceiverExpression(receiverExpression: FirExpression?): Boolean {
if (receiverExpression == null) return false
// Only FirThisReference may be a reference in implicit receiver
val thisReference = receiverExpression.toReference() as? FirThisReference ?: return true
return !thisReference.isImplicit
}
@@ -90,11 +90,13 @@ internal abstract class FirBaseTowerResolveTask(
includeInnerConstructors: Boolean = extensionReceiver != null,
contextReceiverGroup: ContextReceiverGroup? = null,
dispatchReceiverForStatics: ExpressionReceiverValue? = null
): ScopeTowerLevel = ScopeTowerLevel(
components, this,
givenExtensionReceiverOptions = contextReceiverGroup ?: listOfNotNull(extensionReceiver),
withHideMembersOnly, includeInnerConstructors, dispatchReceiverForStatics
)
): ScopeTowerLevel {
return ScopeTowerLevel(
components, this,
givenExtensionReceiverOptions = createExtensionReceiverOptions(contextReceiverGroup, extensionReceiver),
withHideMembersOnly, includeInnerConstructors, dispatchReceiverForStatics
)
}
protected fun FirScope.toScopeTowerLevelForStaticWithImplicitDispatchReceiver(
staticOwnerOwnerSymbol: FirRegularClassSymbol? = null,
@@ -123,7 +125,7 @@ internal abstract class FirBaseTowerResolveTask(
skipSynthetics: Boolean = false,
) = MemberScopeTowerLevel(
components, this,
givenExtensionReceiverOptions = contextReceiverGroup ?: listOfNotNull(extensionReceiver),
givenExtensionReceiverOptions = createExtensionReceiverOptions(contextReceiverGroup, extensionReceiver),
skipSynthetics = skipSynthetics,
)
@@ -132,9 +134,20 @@ internal abstract class FirBaseTowerResolveTask(
otherContextReceiverGroup: ContextReceiverGroup? = null,
) = ContextReceiverGroupMemberScopeTowerLevel(
components, this,
givenExtensionReceiverOptions = otherContextReceiverGroup ?: listOfNotNull(extensionReceiver),
givenExtensionReceiverOptions = createExtensionReceiverOptions(otherContextReceiverGroup, extensionReceiver),
)
private fun createExtensionReceiverOptions(
contextReceiverGroup: ContextReceiverGroup?,
extensionReceiverValue: ReceiverValue?,
): List<FirExpression> {
return when {
contextReceiverGroup != null -> contextReceiverGroup.map { it.receiverExpression }
extensionReceiverValue != null -> listOf(extensionReceiverValue.receiverExpression)
else -> emptyList()
}
}
protected inline fun enumerateTowerLevels(
parentGroup: TowerGroup = TowerGroup.EmptyRoot,
onScope: (FirScope, FirRegularClassSymbol?, TowerGroup) -> Unit,
@@ -117,7 +117,7 @@ class FirTowerResolver(
it,
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER,
scope,
dispatchReceiver,
dispatchReceiver?.receiverExpression,
givenExtensionReceiverOptions = emptyList()
),
context
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.calls.tower
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
@@ -78,11 +79,11 @@ private class TowerScopeLevelProcessor(
) : TowerScopeLevel.TowerScopeLevelProcessor<FirBasedSymbol<*>> {
override fun consumeCandidate(
symbol: FirBasedSymbol<*>,
dispatchReceiverValue: ReceiverValue?,
givenExtensionReceiverOptions: List<ReceiverValue>,
dispatchReceiver: FirExpression?,
givenExtensionReceiverOptions: List<FirExpression>,
scope: FirScope,
objectsByName: Boolean,
isFromOriginalTypeInPresenceOfSmartCast: Boolean
isFromOriginalTypeInPresenceOfSmartCast: Boolean,
) {
resultCollector.consumeCandidate(
group, candidateFactory.createCandidate(
@@ -90,7 +91,7 @@ private class TowerScopeLevelProcessor(
symbol,
explicitReceiverKind,
scope,
dispatchReceiverValue,
dispatchReceiver,
givenExtensionReceiverOptions,
objectsByName,
isFromOriginalTypeInPresenceOfSmartCast
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression
import org.jetbrains.kotlin.fir.expressions.builder.buildResolvedQualifier
import org.jetbrains.kotlin.fir.resolve.*
@@ -55,8 +56,8 @@ abstract class TowerScopeLevel {
interface TowerScopeLevelProcessor<in T : FirBasedSymbol<*>> {
fun consumeCandidate(
symbol: T,
dispatchReceiverValue: ReceiverValue?,
givenExtensionReceiverOptions: List<ReceiverValue>,
dispatchReceiver: FirExpression?,
givenExtensionReceiverOptions: List<FirExpression>,
scope: FirScope,
objectsByName: Boolean = false,
isFromOriginalTypeInPresenceOfSmartCast: Boolean = false,
@@ -74,7 +75,7 @@ abstract class TowerScopeLevel {
class MemberScopeTowerLevel(
private val bodyResolveComponents: BodyResolveComponents,
val dispatchReceiverValue: ReceiverValue,
private val givenExtensionReceiverOptions: List<ReceiverValue>,
private val givenExtensionReceiverOptions: List<FirExpression>,
private val skipSynthetics: Boolean,
) : TowerScopeLevel() {
private val scopeSession: ScopeSession get() = bodyResolveComponents.scopeSession
@@ -156,7 +157,7 @@ class MemberScopeTowerLevel(
empty = false
output.consumeCandidate(
symbol,
dispatchReceiverValue,
dispatchReceiverValue.receiverExpression,
givenExtensionReceiverOptions = emptyList(),
scope
)
@@ -198,8 +199,8 @@ class MemberScopeTowerLevel(
val dispatchReceiverToUse = when {
isFromOriginalTypeInPresenceOfSmartCast ->
getOriginalReceiverExpressionIfStableSmartCast()?.let(::ExpressionReceiverValue)
else -> dispatchReceiverValue
getOriginalReceiverExpressionIfStableSmartCast()
else -> dispatchReceiverValue.receiverExpression
}
output.consumeCandidate(
@@ -283,7 +284,7 @@ class MemberScopeTowerLevel(
}
}
private fun FirCallableSymbol<*>.hasConsistentExtensionReceiver(givenExtensionReceivers: List<ReceiverValue>): Boolean {
private fun FirCallableSymbol<*>.hasConsistentExtensionReceiver(givenExtensionReceivers: List<FirExpression>): Boolean {
return givenExtensionReceivers.isNotEmpty() == hasExtensionReceiver()
}
}
@@ -291,7 +292,7 @@ class MemberScopeTowerLevel(
class ContextReceiverGroupMemberScopeTowerLevel(
bodyResolveComponents: BodyResolveComponents,
contextReceiverGroup: ContextReceiverGroup,
givenExtensionReceiverOptions: List<ReceiverValue> = emptyList(),
givenExtensionReceiverOptions: List<FirExpression> = emptyList(),
) : TowerScopeLevel() {
private val memberScopeLevels = contextReceiverGroup.map {
MemberScopeTowerLevel(bodyResolveComponents, it, givenExtensionReceiverOptions, false)
@@ -320,7 +321,7 @@ class ContextReceiverGroupMemberScopeTowerLevel(
class ScopeTowerLevel(
private val bodyResolveComponents: BodyResolveComponents,
val scope: FirScope,
private val givenExtensionReceiverOptions: List<ReceiverValue>,
private val givenExtensionReceiverOptions: List<FirExpression>,
private val withHideMembersOnly: Boolean,
private val includeInnerConstructors: Boolean,
private val dispatchReceiverForStatics: ExpressionReceiverValue?
@@ -383,7 +384,7 @@ class ScopeTowerLevel(
)
return givenExtensionReceiverOptions.none { extensionReceiver ->
val extensionReceiverType = extensionReceiver.type
val extensionReceiverType = extensionReceiver.resultType.coneType
// If some receiver is non class like, we should not skip it
if (extensionReceiverType !is ConeClassLikeType) return@none true
@@ -417,7 +418,7 @@ class ScopeTowerLevel(
@Suppress("UNCHECKED_CAST")
processor.consumeCandidate(
unwrappedCandidate as T,
dispatchReceiverValue,
dispatchReceiverValue?.receiverExpression,
givenExtensionReceiverOptions,
scope
)
@@ -463,7 +464,7 @@ class ScopeTowerLevel(
scope.processClassifiersByName(info.name) {
empty = false
processor.consumeCandidate(
it, dispatchReceiverValue = null,
it, dispatchReceiver = null,
givenExtensionReceiverOptions = emptyList(),
scope = scope,
objectsByName = true
@@ -10,14 +10,16 @@ import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
import org.jetbrains.kotlin.fir.expressions.FirResolvable
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitExtensionReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext
import org.jetbrains.kotlin.fir.resolve.calls.candidate
import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.replaceStubsAndTypeVariablesToErrors
import org.jetbrains.kotlin.fir.resolve.transformers.FirCallCompletionResultsWriterTransformer
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
import org.jetbrains.kotlin.fir.visitors.transformSingle
@@ -72,12 +74,12 @@ class FirBuilderInferenceSession(
}
private fun Candidate.isSuitableForBuilderInference(): Boolean {
val extensionReceiver = chosenExtensionReceiverValue
val dispatchReceiver = dispatchReceiverValue
val extensionReceiver = chosenExtensionReceiver
val dispatchReceiver = dispatchReceiver
return when {
extensionReceiver == null && dispatchReceiver == null -> false
dispatchReceiver?.type?.containsStubType() == true -> true
extensionReceiver?.type?.containsStubType() == true -> symbol.fir.hasBuilderInferenceAnnotation(session)
dispatchReceiver?.typeRef?.coneType?.containsStubType() == true -> true
extensionReceiver?.typeRef?.coneType?.containsStubType() == true -> symbol.fir.hasBuilderInferenceAnnotation(session)
else -> false
}
}
@@ -251,7 +253,6 @@ class FirBuilderInferenceSession(
}
// TODO: support diagnostics, see [CoroutineInferenceSession#updateCalls]
val completionResultsWriter = components.callCompleter.createCompletionResultsWriter(substitutor)
for ((call, _) in partiallyResolvedCalls) {
call.transformSingle(completionResultsWriter, null)
@@ -260,19 +261,32 @@ class FirBuilderInferenceSession(
}
}
class FirStubTypeTransformer(
private val substitutor: ConeSubstitutor
) : FirDefaultTransformer<Nothing?>() {
class FirStubTypeTransformer(private val substitutor: ConeSubstitutor) : FirDefaultTransformer<Nothing?>() {
override fun <E : FirElement> transformElement(element: E, data: Nothing?): E {
// All resolvable nodes should be implemented separately to cover substitution of receivers in the candidate
if (element is FirResolvable) {
element.candidate()?.let { processCandidate(it) }
}
@Suppress("UNCHECKED_CAST")
return (element.transformChildren(this, data) as E)
return element.transformChildren(this, data = null) as E
}
override fun transformResolvedTypeRef(resolvedTypeRef: FirResolvedTypeRef, data: Nothing?): FirTypeRef =
substitutor.substituteOrNull(resolvedTypeRef.type)?.let {
resolvedTypeRef.withReplacedConeType(it)
} ?: resolvedTypeRef
/*
* We should manually update all receivers in the all not completed candidates, because not all calls with candidates
* contained in partiallyResolvedCalls and candidate stores not receiver values, which are updated, (TODO: remove this comment after removal of updating values)
* and receivers of candidates are not direct FIR children of calls, so they won't be visited during regular transformChildren
*/
private fun processCandidate(candidate: Candidate) {
candidate.dispatchReceiver = candidate.dispatchReceiver?.transform(this, data = null)
candidate.chosenExtensionReceiver = candidate.chosenExtensionReceiver?.transform(this, data = null)
candidate.contextReceiverArguments = candidate.contextReceiverArguments?.map { it.transform(this, data = null) }
}
}
private val BUILDER_INFERENCE_ANNOTATION_CLASS_ID: ClassId = ClassId.topLevel(BUILDER_INFERENCE_ANNOTATION_FQ_NAME)
@@ -72,8 +72,8 @@ class FirDelegatedPropertyInferenceSession(
if (callee.candidate.system.hasContradiction) return true
val hasStubType =
callee.candidate.chosenExtensionReceiverValue?.type?.containsStubType() ?: false
|| callee.candidate.dispatchReceiverValue?.type?.containsStubType() ?: false
callee.candidate.chosenExtensionReceiver?.typeRef?.coneType?.containsStubType() ?: false
|| callee.candidate.dispatchReceiver?.typeRef?.coneType?.containsStubType() ?: false
if (!hasStubType) {
return true
@@ -165,7 +165,6 @@ class FirDelegatedPropertyInferenceSession(
fun completeCandidates(): List<FirResolvable> {
val commonSystem = components.session.inferenceComponents.createConstraintSystem()
@Suppress("UNCHECKED_CAST")
val notCompletedCalls = partiallyResolvedCalls.mapNotNull { partiallyResolvedCall ->
partiallyResolvedCall.first.takeIf { resolvable ->
resolvable.candidate() != null
@@ -13,10 +13,10 @@ import org.jetbrains.kotlin.fir.declarations.impl.FirOuterClassTypeParameterRef
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.diagnostics.*
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.AbstractCallInfo
import org.jetbrains.kotlin.fir.resolve.calls.AbstractCandidate
import org.jetbrains.kotlin.fir.resolve.calls.ReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ResolutionDiagnostic
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
@@ -538,10 +538,10 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
override val applicability: CandidateApplicability
) : AbstractCandidate() {
override val dispatchReceiverValue: ReceiverValue?
override val dispatchReceiver: FirExpression?
get() = null
override val chosenExtensionReceiverValue: ReceiverValue?
override val chosenExtensionReceiver: FirExpression?
get() = null
override val explicitReceiverKind: ExplicitReceiverKind
@@ -9,6 +9,7 @@ import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.labelName
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.*
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
@@ -12,8 +13,8 @@ import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
abstract class AbstractCandidate {
abstract val symbol: FirBasedSymbol<*>
abstract val dispatchReceiverValue: ReceiverValue?
abstract val chosenExtensionReceiverValue: ReceiverValue?
abstract val dispatchReceiver: FirExpression?
abstract val chosenExtensionReceiver: FirExpression?
abstract val explicitReceiverKind: ExplicitReceiverKind
abstract val callInfo: AbstractCallInfo
abstract val diagnostics: List<ResolutionDiagnostic>
@@ -20,6 +20,7 @@ abstract class FirThisReference : FirReference() {
abstract val labelName: String?
abstract val boundSymbol: FirBasedSymbol<*>?
abstract val contextReceiverNumber: Int
abstract val isImplicit: Boolean
override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R = visitor.visitThisReference(this, data)
@@ -23,6 +23,7 @@ internal class FirExplicitThisReference(
override var contextReceiverNumber: Int,
) : FirThisReference() {
override var boundSymbol: FirBasedSymbol<*>? = null
override val isImplicit: Boolean = false
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {}
@@ -23,6 +23,7 @@ internal class FirImplicitThisReference(
) : FirThisReference() {
override val source: KtSourceElement? get() = null
override val labelName: String? get() = null
override val isImplicit: Boolean = true
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {}
@@ -484,6 +484,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
value = "null"
isMutable = true
}
defaultFalse("isImplicit")
}
impl(thisReference, "FirImplicitThisReference") {
@@ -495,6 +496,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
default("boundSymbol") {
isMutable = false
}
defaultTrue("isImplicit")
}
impl(superReference, "FirExplicitSuperReference")
@@ -687,6 +687,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
+stringField("labelName", nullable = true)
+field("boundSymbol", firBasedSymbolType, "*", nullable = true, withReplace = true)
+intField("contextReceiverNumber", withReplace = true)
+booleanField("isImplicit")
}
typeRef.configure {
@@ -1,5 +1,4 @@
// TARGET_BACKEND: JVM
// IGNORE_BACKEND_K2: JVM_IR
// ISSUE: KT-58823
// FILE: Base.java