diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCallResolver.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCallResolver.kt index 976296df066..a432d0a1810 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCallResolver.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCallResolver.kt @@ -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) { diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirVisibilityChecker.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirVisibilityChecker.kt index 27741ed1b53..5d66885f724 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirVisibilityChecker.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirVisibilityChecker.kt @@ -57,9 +57,7 @@ internal class KtFirVisibilityChecker( val dispatchReceiverCanBeExplicit = candidateSymbol is KtCallableSymbol && !candidateSymbol.isExtension val explicitDispatchReceiver = runIf(dispatchReceiverCanBeExplicit) { - receiverExpression - ?.getOrBuildFirSafe(analysisSession.firResolveSession) - ?.let { ExpressionReceiverValue(it) } + receiverExpression?.getOrBuildFirSafe(analysisSession.firResolveSession) } val candidateFirSymbol = candidateSymbol.firSymbol.fir as FirMemberDeclaration diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolver/SingleCandidateResolver.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolver/SingleCandidateResolver.kt index 52f44b298c3..4acafc25447 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolver/SingleCandidateResolver.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolver/SingleCandidateResolver.kt @@ -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, ) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReassignmentAndInvisibleSetterChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReassignmentAndInvisibleSetterChecker.kt index 2267359dbf7..5fdfd7b939f 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReassignmentAndInvisibleSetterChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReassignmentAndInvisibleSetterChecker.kt @@ -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, ) } diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaVisibilityChecker.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaVisibilityChecker.kt index 52fa840a09c..ab956d4b8e5 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaVisibilityChecker.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaVisibilityChecker.kt @@ -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, - dispatchReceiver: ReceiverValue?, + dispatchReceiver: FirExpression?, session: FirSession, isCallToPropertySetter: Boolean, supertypeSupplier: SupertypeSupplier diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt index db8c448c108..91a3848212e 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt @@ -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, - dispatchReceiver: ReceiverValue?, + dispatchReceiver: FirExpression?, session: FirSession, isCallToPropertySetter: Boolean, supertypeSupplier: SupertypeSupplier @@ -95,7 +94,7 @@ abstract class FirVisibilityChecker : FirSessionComponent { session: FirSession, useSiteFile: FirFile, containingDeclarations: List, - 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, 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, - dispatchReceiver: ReceiverValue?, + dispatchReceiver: FirExpression?, isCallToPropertySetter: Boolean = false, supertypeSupplier: SupertypeSupplier ): Boolean { @@ -288,7 +287,7 @@ abstract class FirVisibilityChecker : FirSessionComponent { symbol: FirBasedSymbol<*>, useSiteFile: FirFile, containingDeclarations: List, - dispatchReceiver: ReceiverValue?, + dispatchReceiver: FirExpression?, session: FirSession, isCallToPropertySetter: Boolean, supertypeSupplier: SupertypeSupplier @@ -304,7 +303,7 @@ abstract class FirVisibilityChecker : FirSessionComponent { symbol: FirBasedSymbol<*>, containingDeclarationOfUseSite: List, 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, - dispatchReceiver: ReceiverValue?, + dispatchReceiver: FirExpression?, ownerLookupTag: ConeClassLikeLookupTag, session: FirSession, isVariableOrNamedFunction: Boolean, diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt index 84f0e51609a..d46f6c82b53 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt @@ -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 : Receiver { +abstract class AbstractExplicitReceiver { abstract val explicitReceiver: FirExpression } @@ -62,11 +59,11 @@ abstract class AbstractExplicitReceiverValue : 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(), ReceiverValue { override fun scope(useSiteSession: FirSession, scopeSession: ScopeSession): FirTypeScope? { @@ -121,10 +118,46 @@ sealed class ImplicitReceiverValue>( 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>( @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>( @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, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt index dc866dd356a..fc759f8b92a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt @@ -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, + val givenExtensionReceiverOptions: List, 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? = 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 = contextReceiverArguments ?: emptyList() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt index 2c3779d999d..94e3e8a52c1 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt @@ -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 = emptyList(), + dispatchReceiver: FirExpression? = null, + givenExtensionReceiverOptions: List = 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, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt index ed0afbeb515..c78887c5d5e 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt @@ -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>> = + val receiverGroups: List> = 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() @@ -236,7 +236,7 @@ object CheckContextReceivers : ResolutionStage() { private fun Candidate.findClosestMatchingReceivers( expectedType: ConeKotlinType, - receiverGroups: List>>, + receiverGroups: List>, context: ResolutionContext, ): List { 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.checkImpl( + private fun checkImpl( candidate: Candidate, sink: CheckerSink, context: ResolutionContext, dslMarkersProvider: () -> Set, - 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 { + return CheckDslScopeViolation.getDslMarkersOfImplicitReceiver(boundSymbol, type, context) + } + + private fun getDslMarkersOfImplicitReceiver( + boundSymbol: FirBasedSymbol<*>?, + type: ConeKotlinType, + context: ResolutionContext, + ): Set { 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) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/VisibilityUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/VisibilityUtils.kt index ee0b62d5515..7c54eb7aae1 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/VisibilityUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/VisibilityUtils.kt @@ -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 +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirTowerResolveTask.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirTowerResolveTask.kt index 7bdd4035717..91964c31752 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirTowerResolveTask.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirTowerResolveTask.kt @@ -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 { + 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, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirTowerResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirTowerResolver.kt index 743bc9fc4c0..d1ca4a4c47d 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirTowerResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirTowerResolver.kt @@ -117,7 +117,7 @@ class FirTowerResolver( it, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, scope, - dispatchReceiver, + dispatchReceiver?.receiverExpression, givenExtensionReceiverOptions = emptyList() ), context diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevelHandler.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevelHandler.kt index cd114fb94cb..433505c27ca 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevelHandler.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevelHandler.kt @@ -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> { override fun consumeCandidate( symbol: FirBasedSymbol<*>, - dispatchReceiverValue: ReceiverValue?, - givenExtensionReceiverOptions: List, + dispatchReceiver: FirExpression?, + givenExtensionReceiverOptions: List, 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 diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt index d0b57d4dc7c..0331a5b7fa5 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt @@ -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> { fun consumeCandidate( symbol: T, - dispatchReceiverValue: ReceiverValue?, - givenExtensionReceiverOptions: List, + dispatchReceiver: FirExpression?, + givenExtensionReceiverOptions: List, 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, + private val givenExtensionReceiverOptions: List, 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): Boolean { + private fun FirCallableSymbol<*>.hasConsistentExtensionReceiver(givenExtensionReceivers: List): Boolean { return givenExtensionReceivers.isNotEmpty() == hasExtensionReceiver() } } @@ -291,7 +292,7 @@ class MemberScopeTowerLevel( class ContextReceiverGroupMemberScopeTowerLevel( bodyResolveComponents: BodyResolveComponents, contextReceiverGroup: ContextReceiverGroup, - givenExtensionReceiverOptions: List = emptyList(), + givenExtensionReceiverOptions: List = 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, + private val givenExtensionReceiverOptions: List, 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 diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt index 8afc75e5259..43ff953e0ca 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt @@ -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() { +class FirStubTypeTransformer(private val substitutor: ConeSubstitutor) : FirDefaultTransformer() { override fun 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) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt index cb990e1e42f..a40d8582760 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt @@ -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 { val commonSystem = components.session.inferenceComponents.createConstraintSystem() - @Suppress("UNCHECKED_CAST") val notCompletedCalls = partiallyResolvedCalls.mapNotNull { partiallyResolvedCall -> partiallyResolvedCall.first.takeIf { resolvable -> resolvable.candidate() != null diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt index bb11532c535..9ecd26af141 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt @@ -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 diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/declarations/ImplicitReceiverUtils.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/declarations/ImplicitReceiverUtils.kt index 732f5b17961..6ae3bf5b96b 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/declarations/ImplicitReceiverUtils.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/declarations/ImplicitReceiverUtils.kt @@ -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.* diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/AbstractCandidate.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/AbstractCandidate.kt index 670b79aab39..58dcc79b725 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/AbstractCandidate.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/AbstractCandidate.kt @@ -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 diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/references/FirThisReference.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/references/FirThisReference.kt index 0243c470ab4..cb98eba149e 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/references/FirThisReference.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/references/FirThisReference.kt @@ -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 accept(visitor: FirVisitor, data: D): R = visitor.visitThisReference(this, data) diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/references/impl/FirExplicitThisReference.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/references/impl/FirExplicitThisReference.kt index 7658a48fc3c..110e5759dca 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/references/impl/FirExplicitThisReference.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/references/impl/FirExplicitThisReference.kt @@ -23,6 +23,7 @@ internal class FirExplicitThisReference( override var contextReceiverNumber: Int, ) : FirThisReference() { override var boundSymbol: FirBasedSymbol<*>? = null + override val isImplicit: Boolean = false override fun acceptChildren(visitor: FirVisitor, data: D) {} diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/references/impl/FirImplicitThisReference.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/references/impl/FirImplicitThisReference.kt index 07a64825932..c53658c6845 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/references/impl/FirImplicitThisReference.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/references/impl/FirImplicitThisReference.kt @@ -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 acceptChildren(visitor: FirVisitor, data: D) {} diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/ImplementationConfigurator.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/ImplementationConfigurator.kt index 80cce5f004f..36861c936dd 100644 --- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/ImplementationConfigurator.kt +++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/ImplementationConfigurator.kt @@ -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") diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt index 8c938480d56..dbacfc60535 100644 --- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt +++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt @@ -687,6 +687,7 @@ object NodeConfigurator : AbstractFieldConfigurator(FirTreeBuild +stringField("labelName", nullable = true) +field("boundSymbol", firBasedSymbolType, "*", nullable = true, withReplace = true) +intField("contextReceiverNumber", withReplace = true) + +booleanField("isImplicit") } typeRef.configure { diff --git a/compiler/testData/codegen/box/smartCasts/smartcastOnImplicitDispatchReceiver.kt b/compiler/testData/codegen/box/smartCasts/smartcastOnImplicitDispatchReceiver.kt index 90e60170b31..03728c757c2 100644 --- a/compiler/testData/codegen/box/smartCasts/smartcastOnImplicitDispatchReceiver.kt +++ b/compiler/testData/codegen/box/smartCasts/smartcastOnImplicitDispatchReceiver.kt @@ -1,5 +1,4 @@ // TARGET_BACKEND: JVM -// IGNORE_BACKEND_K2: JVM_IR // ISSUE: KT-58823 // FILE: Base.java