diff --git a/.idea/dictionaries/Denis_Zharkov.xml b/.idea/dictionaries/Denis_Zharkov.xml new file mode 100644 index 00000000000..3251fd1e4bd --- /dev/null +++ b/.idea/dictionaries/Denis_Zharkov.xml @@ -0,0 +1,7 @@ + + + + pcla + + + \ No newline at end of file diff --git a/compiler/fir/build.gradle.kts b/compiler/fir/build.gradle.kts index aff8f2e0fa6..af70e2c8ae4 100644 --- a/compiler/fir/build.gradle.kts +++ b/compiler/fir/build.gradle.kts @@ -22,6 +22,7 @@ subprojects { tasks.withType>().configureEach { kotlinOptions { freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.fir.symbols.SymbolInternals" + freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.types.model.K2Only" } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt index 0cc76096512..2ddeb0bb9ad 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt @@ -316,13 +316,13 @@ private fun mapInapplicableCandidateError( rootCause.expectedContextReceiverType.removeTypeVariableTypes(typeContext) ) - is StubBuilderInferenceReceiver -> { - val typeParameterSymbol = rootCause.typeParameterSymbol + is TypeVariableAsExplicitReceiver -> { + val typeParameter = rootCause.typeParameter @OptIn(SymbolInternals::class) FirErrors.BUILDER_INFERENCE_STUB_RECEIVER.createOn( - qualifiedAccessSource ?: source, - typeParameterSymbol.name, - (typeParameterSymbol.containingDeclarationSymbol.fir as FirMemberDeclaration).nameOrSpecialName + rootCause.explicitReceiver.source, + typeParameter.symbol.name, + (typeParameter.symbol.containingDeclarationSymbol.fir as FirMemberDeclaration).nameOrSpecialName ) } diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeVariableAndStubTypes.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeVariableAndStubTypes.kt index 3444ca88723..85eae6a1078 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeVariableAndStubTypes.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeVariableAndStubTypes.kt @@ -46,6 +46,8 @@ class ConeTypeVariableTypeConstructor( fun recordInfoAboutTypeVariableUsagesAsInvariantOrContravariantParameter() { isContainedInInvariantOrContravariantPositions = true } + + override fun toString(): String = "${this::class.simpleName}($debugName)" } // ----------------------------------- Stub types ----------------------------------- diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt index 9878b0f2b76..f0d695d6c37 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt @@ -125,6 +125,13 @@ private fun ConeKotlinType.scope( is ConeDefinitelyNotNullType -> original.scope(useSiteSession, scopeSession, requiredMembersPhase) is ConeIntegerConstantOperatorType -> scopeSession.getOrBuildScopeForIntegerConstantOperatorType(useSiteSession, this) is ConeIntegerLiteralConstantType -> error("ILT should not be in receiver position") + // See testData/diagnostics/tests/inference/builderInference/memberScopeOfCapturedTypeForPostponedCall.kt + is ConeCapturedType -> { + val supertypes = + constructor.supertypes?.takeIf { it.isNotEmpty() } + ?: listOf(useSiteSession.builtinTypes.anyType.type) + useSiteSession.typeContext.intersectTypes(supertypes).scope(useSiteSession, scopeSession, requiredMembersPhase) + } else -> null } 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 d0290e8dc7c..9c6b447201c 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 @@ -9,7 +9,6 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.fakeElement import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirResolvePhase -import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic import org.jetbrains.kotlin.fir.expressions.FirCheckNotNullCall import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression @@ -18,7 +17,6 @@ import org.jetbrains.kotlin.fir.expressions.builder.buildInaccessibleReceiverExp import org.jetbrains.kotlin.fir.expressions.builder.buildSmartCastExpression import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpression import org.jetbrains.kotlin.fir.references.builder.buildImplicitThisReference -import org.jetbrains.kotlin.fir.renderWithType import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.scope import org.jetbrains.kotlin.fir.resolve.smartcastScope @@ -28,8 +26,10 @@ import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirScriptSymbol -import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.constructType +import org.jetbrains.kotlin.fir.types.resolvedType import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.SmartcastStability @@ -89,6 +89,7 @@ sealed class ImplicitReceiverValue>( abstract val isContextReceiver: Boolean + // Type before smart cast val originalType: ConeKotlinType = type var implicitScope: FirTypeScope? = @@ -145,19 +146,6 @@ sealed class ImplicitReceiverValue>( @RequiresOptIn annotation class ImplicitReceiverInternals - @Deprecated(level = DeprecationLevel.ERROR, message = "Builder inference should not modify implicit receivers. KT-54708") - fun updateTypeInBuilderInference(type: ConeKotlinType) { - this.type = type - originalReceiverExpression = receiverExpression(boundSymbol, type, contextReceiverNumber, inaccessibleReceiver) - _receiverExpression = null - implicitScope = type.scope( - useSiteSession = useSiteSession, - scopeSession = scopeSession, - callableCopyTypeCalculator = CallableCopyTypeCalculator.DoNothing, - requiredMembersPhase = FirResolvePhase.STATUS, - ) - } - /* * Should be called only in ImplicitReceiverStack */ diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt index 19cec665ac6..33b4d98c7ae 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt @@ -14,10 +14,7 @@ import org.jetbrains.kotlin.fir.resolve.createSubstitutionForSupertype import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider -import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor -import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap -import org.jetbrains.kotlin.fir.resolve.substitution.NoSubstitutor -import org.jetbrains.kotlin.fir.resolve.substitution.createTypeSubstitutorByTypeConstructor +import org.jetbrains.kotlin.fir.resolve.substitution.* import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol @@ -205,6 +202,10 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo return this.intersectedTypes.any { it.containsInternal(predicate) } } + if (this is ConeCapturedType) { + return this.constructor.projection.type?.containsInternal(predicate) == true + } + repeat(argumentsCount()) { index -> val argument = getArgument(index) if (!argument.isStarProjection() && argument.getType().containsInternal(predicate)) return true @@ -358,6 +359,15 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo return ConeSubstitutor.Empty } + override fun createSubstitutionFromSubtypingStubTypesToTypeVariables(): TypeSubstitutorMarker { + return object : AbstractConeSubstitutor(this) { + override fun substituteType(type: ConeKotlinType): ConeKotlinType? { + return ((type as? ConeStubTypeForTypeVariableInSubtyping) + ?.constructor?.variable?.defaultType)?.withNullability(type.nullability, this@ConeInferenceContext) + } + } + } + override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker { if (this === NoSubstitutor) return type require(this is ConeSubstitutor) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt index 63aff9e8212..3fb806689f5 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt @@ -26,8 +26,8 @@ import org.jetbrains.kotlin.fir.resolve.calls.tower.FirTowerResolver import org.jetbrains.kotlin.fir.resolve.calls.tower.TowerGroup import org.jetbrains.kotlin.fir.resolve.calls.tower.TowerResolveManager import org.jetbrains.kotlin.fir.resolve.diagnostics.* -import org.jetbrains.kotlin.fir.resolve.inference.FirBuilderInferenceSession import org.jetbrains.kotlin.fir.resolve.inference.ResolvedCallableReferenceAtom +import org.jetbrains.kotlin.fir.resolve.inference.csBuilder import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer @@ -398,10 +398,11 @@ class FirCallResolver( } fun resolveCallableReference( - constraintSystemBuilder: ConstraintSystemBuilder, + containingCallCandidate: Candidate, resolvedCallableReferenceAtom: ResolvedCallableReferenceAtom, hasSyntheticOuterCall: Boolean, - ): Pair { + ): Pair = components.context.inferenceSession.runCallableReferenceResolution(containingCallCandidate) { + val constraintSystemBuilder = containingCallCandidate.csBuilder val callableReferenceAccess = resolvedCallableReferenceAtom.reference val calleeReference = callableReferenceAccess.calleeReference val lhs = resolvedCallableReferenceAtom.lhs @@ -450,7 +451,7 @@ class FirCallResolver( calleeReference.source ) resolvedCallableReferenceAtom.resultingReference = errorReference - return applicability to false + return@runCallableReferenceResolution applicability to false } reducedCandidates.size > 1 -> { if (resolvedCallableReferenceAtom.hasBeenPostponed) { @@ -460,10 +461,10 @@ class FirCallResolver( calleeReference.source ) resolvedCallableReferenceAtom.resultingReference = errorReference - return applicability to false + return@runCallableReferenceResolution applicability to false } resolvedCallableReferenceAtom.hasBeenPostponed = true - return applicability to true + return@runCallableReferenceResolution applicability to true } } @@ -486,7 +487,7 @@ class FirCallResolver( resolvedCallableReferenceAtom.resultingReference = reference resolvedCallableReferenceAtom.resultingTypeForCallableReference = chosenCandidate.resultingTypeForCallableReference - return applicability to true + return@runCallableReferenceResolution applicability to true } fun resolveDelegatingConstructorCall( @@ -859,7 +860,7 @@ class FirCallResolver( * can be important in builder inference mode, and it will never work if we skip completion here. * See inferenceFromLambdaReturnStatement.kt test. */ - if (components.context.inferenceSession !is FirBuilderInferenceSession && + if (!candidate.usedOuterCs && createResolvedReferenceWithoutCandidateForLocalVariables && explicitReceiver?.resolvedType !is ConeIntegerLiteralType && coneSymbol is FirVariableSymbol && diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirOverloadByLambdaReturnTypeResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirOverloadByLambdaReturnTypeResolver.kt index 790bff453f1..8d7e82f4639 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirOverloadByLambdaReturnTypeResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirOverloadByLambdaReturnTypeResolver.kt @@ -39,26 +39,6 @@ class FirOverloadByLambdaReturnTypeResolver( ): Set where T : FirStatement, T : FirResolvable { if (bestCandidates.size <= 1) return bestCandidates - /* - * Inference session may look into candidate of call, and for that it uses callee reference. - * So we need replace reference with proper candidate before calling inference session - */ - val shouldRunCompletion = if (inferenceSession != FirInferenceSession.DEFAULT) { - var shouldRunCompletion = true - val originalReference = qualifiedAccess.calleeReference - for (candidate in bestCandidates) { - qualifiedAccess.replaceCalleeReference(FirNamedReferenceWithCandidate(null, candidate.callInfo.name, candidate)) - shouldRunCompletion = shouldRunCompletion && inferenceSession.shouldRunCompletion(qualifiedAccess) - if (!shouldRunCompletion) break - } - qualifiedAccess.replaceCalleeReference(originalReference) - shouldRunCompletion - } else { - true - } - - if (!shouldRunCompletion) return bestCandidates - return reduceCandidatesImpl( qualifiedAccess, bestCandidates, @@ -137,7 +117,7 @@ class FirOverloadByLambdaReturnTypeResolver( firstCandidate.system, firstAtom, firstCandidate, - ConstraintSystemCompletionMode.FULL, + forOverloadByLambdaReturnType = true, ) while (iterator.hasNext()) { val (candidate, atom) = iterator.next() @@ -147,7 +127,6 @@ class FirOverloadByLambdaReturnTypeResolver( atom, candidate, results, - ConstraintSystemCompletionMode.FULL, ) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirSpecialTowerDataContexts.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirSpecialTowerDataContexts.kt index 87dbcbaaf2b..7884c61df8e 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirSpecialTowerDataContexts.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirSpecialTowerDataContexts.kt @@ -7,43 +7,54 @@ package org.jetbrains.kotlin.fir.resolve import org.jetbrains.kotlin.fir.declarations.FirTowerDataContext import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess +import org.jetbrains.kotlin.fir.resolve.inference.FirInferenceSession import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousFunctionSymbol class FirSpecialTowerDataContexts { - private val towerDataContextForAnonymousFunctions: MutableMap = mutableMapOf() - private val towerDataContextForCallableReferences: MutableMap = mutableMapOf() + private val contextForAnonymousFunctions: MutableMap = mutableMapOf() + private val contextForCallableReferences: MutableMap = mutableMapOf() - fun getAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol): FirTowerDataContext? { - return towerDataContextForAnonymousFunctions[symbol] + fun getAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol): PostponedAtomsResolutionContext? { + return contextForAnonymousFunctions[symbol] } - fun getCallableReferenceContext(access: FirCallableReferenceAccess): FirTowerDataContext? { - return towerDataContextForCallableReferences[access] + fun getCallableReferenceContext(access: FirCallableReferenceAccess): PostponedAtomsResolutionContext? { + return contextForCallableReferences[access] } - fun storeAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol, context: FirTowerDataContext) { - towerDataContextForAnonymousFunctions[symbol] = context + fun storeAnonymousFunctionContext( + symbol: FirAnonymousFunctionSymbol, + context: FirTowerDataContext, + inferenceSession: FirInferenceSession, + ) { + contextForAnonymousFunctions[symbol] = Pair(context, inferenceSession) } fun dropAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol) { - towerDataContextForAnonymousFunctions.remove(symbol) + contextForAnonymousFunctions.remove(symbol) } - fun storeCallableReferenceContext(access: FirCallableReferenceAccess, context: FirTowerDataContext) { - towerDataContextForCallableReferences[access] = context + fun storeCallableReferenceContext( + access: FirCallableReferenceAccess, + context: FirTowerDataContext, + inferenceSession: FirInferenceSession + ) { + contextForCallableReferences[access] = Pair(context, inferenceSession) } fun dropCallableReferenceContext(access: FirCallableReferenceAccess) { - towerDataContextForCallableReferences.remove(access) + contextForCallableReferences.remove(access) } fun putAll(contexts: FirSpecialTowerDataContexts) { - towerDataContextForCallableReferences.putAll(contexts.towerDataContextForCallableReferences) - towerDataContextForAnonymousFunctions.putAll(contexts.towerDataContextForAnonymousFunctions) + contextForCallableReferences.putAll(contexts.contextForCallableReferences) + contextForAnonymousFunctions.putAll(contexts.contextForAnonymousFunctions) } fun clear() { - towerDataContextForAnonymousFunctions.clear() - towerDataContextForCallableReferences.clear() + contextForAnonymousFunctions.clear() + contextForCallableReferences.clear() } } + +typealias PostponedAtomsResolutionContext = Pair \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt index 8af883c6578..723dc4c3ba8 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt @@ -28,7 +28,11 @@ sealed class ResolutionMode( } data object ContextIndependent : ResolutionMode(forceFullCompletion = true) - data object ReceiverResolution : ResolutionMode(forceFullCompletion = true) + + sealed class ReceiverResolution(val forCallableReference: Boolean) : ResolutionMode(forceFullCompletion = true) { + data object ForCallableReference : ReceiverResolution(forCallableReference = true) + companion object : ReceiverResolution(forCallableReference = false) + } class WithExpectedType( val expectedTypeRef: FirResolvedTypeRef, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt index cbe6b482808..8135ac139e3 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext +import org.jetbrains.kotlin.types.model.safeSubstitute import org.jetbrains.kotlin.types.model.typeConstructor val SAM_LOOKUP_NAME = Name.special("") @@ -190,7 +191,7 @@ fun Candidate.resolveSubCallArgument( * placeholder type with value 0, but argument contains type with proper literal value */ val type: ConeKotlinType = context.returnTypeCalculator.tryCalculateReturnType(candidate.symbol.fir as FirCallableDeclaration).type - val argumentType = candidate.substitutor.substituteOrSelf(type) + val argumentType = candidate.substitutor.substituteOrSelf(type).prepareTypeForPartiallyCompletedPCLACall(candidate, csBuilder) resolvePlainArgumentType( csBuilder, argument, @@ -204,6 +205,26 @@ fun Candidate.resolveSubCallArgument( ) } +// For PCLA/Delegate inference partially completed calls, there might be a situation when +// - There are already fixed variables (because we need them for lambda analysis) +// - They are used in return types +// +// In this case, we substitute them explicitly because otherwise +// TypeCheckerStateForConstraintInjector.fixedTypeVariable throws an exception. +// +// Note that this is not relevant outside PCLA context because +// - For partial completion, we avoid fixing TVs that are used inside return types +// - For FULL completion, we would run completion results writing, so there would be no candidates and type variables inside return types. +// +// See singleBranchConditionLastStatementInLambda.kt and assignmentUsingIncompletePCLACall.kt tests +private fun ConeKotlinType.prepareTypeForPartiallyCompletedPCLACall( + candidate: Candidate, outerCSBuilder: ConstraintSystemBuilder +): ConeKotlinType { + if (!candidate.system.usesOuterCs) return this + + return outerCSBuilder.buildCurrentSubstitutor().safeSubstitute(candidate.callInfo.session.typeContext, this) as ConeKotlinType +} + fun Candidate.resolvePlainExpressionArgument( csBuilder: ConstraintSystemBuilder, argument: FirExpression, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt index 88dbaf006f3..453767277dc 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt @@ -24,6 +24,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) { ConstraintSystemForks, CheckIncompatibleTypeVariableUpperBounds, TypeParameterAsCallable, + TypeVariablesInExplicitReceivers, ) object SyntheticSelect : CallKind( @@ -59,6 +60,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) { ConstraintSystemForks, CheckIncompatibleTypeVariableUpperBounds, TypeParameterAsCallable, + TypeVariablesInExplicitReceivers, ) object DelegatingConstructorCall : CallKind( @@ -94,6 +96,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) { CheckIncompatibleTypeVariableUpperBounds, ProcessDynamicExtensionAnnotation, LowerPriorityIfDynamic, + TypeVariablesInExplicitReceivers, ) object SyntheticIdForCallableReferencesResolution : CallKind( 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 30304a49996..7e794a69c43 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 @@ -7,12 +7,12 @@ package org.jetbrains.kotlin.fir.resolve.calls import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.fakeElement +import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction import org.jetbrains.kotlin.fir.declarations.FirValueParameter -import org.jetbrains.kotlin.fir.expressions.FirExpression -import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression -import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression +import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpressionCopy import org.jetbrains.kotlin.fir.expressions.impl.FirExpressionStub +import org.jetbrains.kotlin.fir.resolve.inference.FirInferenceSession import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents import org.jetbrains.kotlin.fir.resolve.inference.PostponedResolvedAtom import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability import org.jetbrains.kotlin.resolve.calls.tower.isSuccess import org.jetbrains.kotlin.util.CodeFragmentAdjustment +import org.jetbrains.kotlin.utils.addToStdlib.runUnless class Candidate( symbol: FirBasedSymbol<*>, @@ -47,6 +48,7 @@ class Candidate( val isFromCompanionObjectTypeScope: Boolean = false, // It's only true if we're in the member scope of smart cast receiver and this particular candidate came from original type val isFromOriginalTypeInPresenceOfSmartCast: Boolean = false, + inferenceSession: FirInferenceSession, ) : AbstractCandidate() { override var symbol: FirBasedSymbol<*> = symbol @@ -66,10 +68,21 @@ class Candidate( this.symbol = symbol } + val usedOuterCs: Boolean get() = system.usesOuterCs + private var systemInitialized: Boolean = false val system: NewConstraintSystemImpl by lazy(LazyThreadSafetyMode.NONE) { val system = constraintSystemFactory.createConstraintSystem() - system.setBaseSystem(baseSystem) + + val baseCSFromInferenceSession = + runUnless(baseSystem.usesOuterCs) { inferenceSession.baseConstraintStorageForCandidate(this) } + if (baseCSFromInferenceSession != null) { + system.setBaseSystem(baseCSFromInferenceSession) + system.addOtherSystem(baseSystem) + } else { + system.setBaseSystem(baseSystem) + } + systemInitialized = true system } @@ -102,6 +115,9 @@ class Candidate( var functionTypesOfSamConversions: HashMap? = null lateinit var typeArgumentMapping: TypeArgumentMapping val postponedAtoms = mutableListOf() + val postponedPCLACalls = mutableListOf() + val lambdasAnalyzedWithPCLA = mutableListOf() + val onCompletionResultsWritingCallbacks = mutableListOf<(ConeSubstitutor) -> Unit>() var currentApplicability = CandidateApplicability.RESOLVED private set 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 fc39b977bb0..cb6e2a62c7c 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 @@ -35,7 +35,6 @@ class CandidateFactory private constructor( companion object { private fun buildBaseSystem(context: ResolutionContext, callInfo: CallInfo): ConstraintStorage { val system = context.inferenceComponents.createConstraintSystem() - system.addOuterSystem(context.bodyResolveContext.outerConstraintStorage) callInfo.arguments.forEach { system.addSubsystemFromExpression(it) } @@ -79,6 +78,7 @@ class CandidateFactory private constructor( ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> false }, isFromOriginalTypeInPresenceOfSmartCast, + context.bodyResolveContext.inferenceSession, ) // The counterpart in FE 1.0 checks if the given descriptor is VariableDescriptor yet not PropertyDescriptor. @@ -165,6 +165,7 @@ class CandidateFactory private constructor( baseSystem, callInfo, originScope = null, + inferenceSession = context.bodyResolveContext.inferenceSession, ) } @@ -194,25 +195,47 @@ class CandidateFactory private constructor( } } -fun PostponedArgumentsAnalyzerContext.addSubsystemFromExpression(statement: FirStatement): Boolean { +fun processConstraintStorageFromExpression(statement: FirStatement, processor: (ConstraintStorage) -> Unit): Boolean { return when (statement) { is FirQualifiedAccessExpression, is FirWhenExpression, is FirTryExpression, is FirCheckNotNullCall, - is FirElvisExpression -> { + is FirElvisExpression, + -> { val candidate = (statement as FirResolvable).candidate() ?: return false - addOtherSystem(candidate.system.asReadOnlyStorage()) + processor(candidate.system.asReadOnlyStorage()) true } - is FirSafeCallExpression -> addSubsystemFromExpression(statement.selector) - is FirWrappedArgumentExpression -> addSubsystemFromExpression(statement.expression) - is FirBlock -> statement.returnExpressions().any { addSubsystemFromExpression(it) } + is FirSafeCallExpression -> processConstraintStorageFromExpression(statement.selector, processor) + is FirWrappedArgumentExpression -> processConstraintStorageFromExpression(statement.expression, processor) + is FirBlock -> { + var wasAny = false + + // Might be `.any {` call, but we should process all the items + statement.returnExpressions().forEach { + if (processConstraintStorageFromExpression(it, processor)) { + wasAny = true + } + } + + wasAny + } else -> false } } +fun PostponedArgumentsAnalyzerContext.addSubsystemFromExpression(statement: FirStatement): Boolean { + return processConstraintStorageFromExpression(statement) { + // If a call inside a lambda uses outer CS, + // it's already integrated into inference session via FirPCLAInferenceSession.processPartiallyResolvedCall + if (!it.usesOuterCs) { + addOtherSystem(it) + } + } +} + internal fun FirResolvable.candidate(): Candidate? { return when (val callee = this.calleeReference) { is FirNamedReferenceWithCandidate -> return callee.candidate 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 090769996a4..61fd9c34200 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 @@ -123,21 +123,6 @@ object CheckExtensionReceiver : ResolutionStage() { candidate.chosenExtensionReceiver = receiver.expression - val checkBuilderInferenceRestriction = - !context.session.languageVersionSettings - .supportsFeature(LanguageFeature.NoBuilderInferenceWithoutAnnotationRestriction) - if (checkBuilderInferenceRestriction) { - val resolvedType = receiver.expression.resolvedType - if (resolvedType is ConeStubTypeForChainInference) { - val typeVariable = resolvedType.constructor.variable - sink.yieldDiagnostic( - StubBuilderInferenceReceiver( - (typeVariable.typeConstructor.originalTypeParameter as ConeTypeParameterLookupTag).typeParameterSymbol - ) - ) - } - } - sink.yieldIfNeed() } @@ -264,6 +249,39 @@ object CheckContextReceivers : ResolutionStage() { } } +object TypeVariablesInExplicitReceivers : ResolutionStage() { + override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) { + if (callInfo.callSite.isAnyOfDelegateOperators()) return + + val explicitReceiver = callInfo.explicitReceiver ?: return checkOtherCases(candidate) + + val typeVariableType = explicitReceiver.resolvedType.obtainTypeVariable() ?: return checkOtherCases(candidate) + val typeParameter = + (typeVariableType.typeConstructor.originalTypeParameter as? ConeTypeParameterLookupTag)?.typeParameterSymbol?.fir + ?: return checkOtherCases(candidate) + + sink.reportDiagnostic(TypeVariableAsExplicitReceiver(explicitReceiver, typeParameter)) + } + + private fun checkOtherCases(candidate: Candidate) { + require(candidate.chosenExtensionReceiverExpression()?.resolvedType?.obtainTypeVariable() == null) { + "Found TV in extension receiver of $candidate" + } + + require(candidate.dispatchReceiverExpression()?.resolvedType?.obtainTypeVariable() == null) { + "Found TV in dispatch receiver of $candidate" + } + } + + private fun ConeKotlinType.obtainTypeVariable(): ConeTypeVariableType? = when (this) { + is ConeFlexibleType -> lowerBound.obtainTypeVariable() + is ConeTypeVariableType -> this + is ConeDefinitelyNotNullType -> original.obtainTypeVariable() + is ConeIntersectionType -> intersectedTypes.firstNotNullOfOrNull { it.obtainTypeVariable() } + else -> null + } +} + private fun Candidate.findClosestMatchingReceivers( expectedType: ConeKotlinType, receiverGroups: List>, @@ -569,7 +587,7 @@ internal object EagerResolveOfCallableReferences : CheckerStage() { if (atom is ResolvedCallableReferenceAtom) { val (applicability, success) = context.bodyResolveComponents.callResolver.resolveCallableReference( - candidate.csBuilder, atom, hasSyntheticOuterCall = candidate.callInfo.name == ACCEPT_SPECIFIC_TYPE.callableName + candidate, atom, hasSyntheticOuterCall = candidate.callInfo.name == ACCEPT_SPECIFIC_TYPE.callableName ) if (!success) { // If the resolution was unsuccessful, we ensure that an error will be reported for the callable reference diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt index afd22feaebc..9084c6f4a36 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.references.symbol import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue +import org.jetbrains.kotlin.fir.resolve.calls.candidate import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap @@ -30,7 +31,10 @@ import org.jetbrains.kotlin.fir.scopes.getFunctions import org.jetbrains.kotlin.fir.scopes.impl.toConeType import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol -import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.StandardClassIds @@ -926,6 +930,11 @@ abstract class FirDataFlowAnalyzer( private fun FirStatement.orderedArguments(callee: FirFunction): Array? { fun FirQualifiedAccessExpression.firstReceiver(): FirExpression? { + val candidate = candidate() + // Processing case with a candidate might be necessary for PCLA, because even top-level calls might be not fully completed + if (candidate != null) { + return candidate.chosenExtensionReceiverExpression() ?: candidate.dispatchReceiverExpression() + } return extensionReceiver ?: dispatchReceiver } @@ -937,7 +946,8 @@ abstract class FirDataFlowAnalyzer( return when (this) { is FirFunctionCall -> { - val argumentToParameter = resolvedArgumentMapping ?: return null + // Processing case with a candidate might be necessary for PCLA, because even top-level calls might be not fully completed + val argumentToParameter = resolvedArgumentMapping ?: candidate()?.argumentMapping ?: return null val parameterToArgument = argumentToParameter.entries.associate { it.value to it.key.unwrapArgument() } Array(callee.valueParameters.size + 1) { i -> if (i > 0) parameterToArgument[callee.valueParameters[i - 1]] else receiver @@ -1155,7 +1165,11 @@ abstract class FirDataFlowAnalyzer( } private fun exitBooleanNot(flow: MutableFlow, expression: FirFunctionCall) { - val argumentVariable = variableStorage.get(flow, expression.dispatchReceiver!!) ?: return + val argumentVariable = variableStorage.get( + flow, + // Processing case with a candidate might be necessary for PCLA, because even top-level calls might be not fully completed + expression.candidate()?.dispatchReceiverExpression() ?: expression.dispatchReceiver!! + ) ?: return val expressionVariable = variableStorage.createSynthetic(expression) // Alternatively: (expression == true => argument == false) && (expression == false => argument == true) // Which implementation is faster and/or consumes less memory is an open question. diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt index add073684a3..9802589f9c4 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt @@ -23,10 +23,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol import org.jetbrains.kotlin.fir.types.ConeErrorType import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeTypeVariable -import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionContext -import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode -import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDependencyInformationProvider -import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator +import org.jetbrains.kotlin.resolve.calls.inference.components.* import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl import org.jetbrains.kotlin.resolve.calls.inference.model.NotEnoughInformationForTypeParameter import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints @@ -81,9 +78,9 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c if (analyzeArgumentWithFixedParameterTypes(languageVersionSettings, postponedArguments, analyze)) continue - val isThereAnyReadyForFixationVariable = variableFixationFinder.findFirstVariableForFixation( - this, - getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms), + val isThereAnyReadyForFixationVariable = findFirstVariableForFixation( + collectVariablesFromContext, + topLevelAtoms, postponedArguments, completionMode, topLevelType @@ -113,7 +110,7 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c if (wasBuiltNewExpectedTypeForSomeArgument) continue - if (completionMode == ConstraintSystemCompletionMode.FULL) { + if (completionMode.allLambdasShouldBeAnalyzed) { // Stage 3: fix variables for parameter types of all postponed arguments for (argument in postponedArguments) { val variableWasFixed = postponedArgumentsInputTypesResolver.fixNextReadyVariableForParameterTypeIfNeeded( @@ -152,21 +149,33 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c continue // Stage 7: try to complete call with the builder inference if there are uninferred type variables - val allTypeVariables = getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms) - val areThereAppearedProperConstraintsForSomeVariable = tryToCompleteWithBuilderInference( - completionMode, topLevelType, postponedArguments, allTypeVariables, analyze + val areThereAppearedProperConstraintsForSomeVariable = tryToCompleteWithPCLA( + completionMode, postponedArguments, analyze, ) if (areThereAppearedProperConstraintsForSomeVariable) continue + if (completionMode == ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL) { + // Complete all lambdas, maybe with fixing type variables used as top-level input types. + // It's necessary because we need to process all data-flow info before going to the next statement. + if (analyzeRemainingNotAnalyzedPostponedArgument(postponedArguments, analyze)) + continue + } + // Stage 8: report "not enough information" for uninferred type variables reportNotEnoughTypeInformation( - completionMode, topLevelAtoms, topLevelType, allTypeVariables, postponedArguments + completionMode, topLevelAtoms, topLevelType, postponedArguments ) + if (completionMode == ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL) { + reportNotEnoughInformationForTypeVariablesRequiredForInputTypesOfLambdas( + postponedArguments, topLevelType, dependencyProvider, topLevelAtoms + ) + } + // Stage 9: force analysis of remaining not analyzed postponed arguments and rerun stages if there are - if (completionMode == ConstraintSystemCompletionMode.FULL) { + if (completionMode.allLambdasShouldBeAnalyzed) { if (analyzeRemainingNotAnalyzedPostponedArgument(postponedArguments, analyze)) continue } @@ -175,20 +184,61 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c } } + private fun ConstraintSystemCompletionContext.reportNotEnoughInformationForTypeVariablesRequiredForInputTypesOfLambdas( + postponedArguments: List, + topLevelType: ConeKotlinType, + dependencyProvider: TypeVariableDependencyInformationProvider, + topLevelAtoms: List, + ) { + for (argument in postponedArguments) { + val variableForFixation = postponedArgumentsInputTypesResolver.findNextVariableForReportingNotInferredInputType( + this, + argument, + postponedArguments, + topLevelType, + dependencyProvider, + ) ?: continue + + assert(!variableForFixation.isReady) { + "At this stage there should be no remaining variables with proper constraints from input types" + } + + val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) + processVariableWhenNotEnoughInformation(variableWithConstraints, topLevelAtoms) + } + } + + private fun ConstraintSystemCompletionContext.findFirstVariableForFixation( + collectVariablesFromContext: Boolean, + topLevelAtoms: List, + postponedArguments: List, + completionMode: ConstraintSystemCompletionMode, + topLevelType: ConeKotlinType, + ): VariableFixationFinder.VariableForFixation? { + return variableFixationFinder.findFirstVariableForFixation( + this, + getOrderedAllTypeVariables( + collectVariablesFromContext, + topLevelAtoms + ), + postponedArguments, + completionMode, + topLevelType + ) + } + /** * General documentation for builder inference algorithm is located at `/docs/fir/builder_inference.md` * * This function checks if any of the postponed arguments are suitable for builder inference, and performs it for all eligible lambda arguments * @return true if we got new proper constraints after builder inference */ - private fun ConstraintSystemCompletionContext.tryToCompleteWithBuilderInference( + private fun ConstraintSystemCompletionContext.tryToCompleteWithPCLA( completionMode: ConstraintSystemCompletionMode, - topLevelType: ConeKotlinType, postponedArguments: List, - allTypeVariables: List, analyze: (PostponedResolvedAtom) -> Unit, ): Boolean { - if (completionMode != ConstraintSystemCompletionMode.FULL) return false + if (!completionMode.allLambdasShouldBeAnalyzed) return false // If we use the builder inference anyway (if the annotation is presented), then we are already analysed builder inference lambdas if (!languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceOnlyIfNeeded)) return false @@ -198,56 +248,17 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c fun ResolvedLambdaAtom.notFixedInputTypeVariables(): List = inputTypes.flatMap { it.extractTypeVariables() }.filter { it !in fixedTypeVariables } - val checkForDangerousBuilderInference = - !languageVersionSettings.supportsFeature(LanguageFeature.NoBuilderInferenceWithoutAnnotationRestriction) - - // Let's call builder lambda (BL) a lambda that has non-zero not fixed input type variables in - // type arguments of it's input types - // ex: MutableList.() -> Unit - // During type inference of call-site such lambda will be considered BL, if - // T not fixed yet - // Given we have two or more builder lambdas among postponed arguments, it could result in incorrect type inference due to - // incorrect constraint propagation into common system - // See KT-53740 - // Constraint propagation into common system happens at - // org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer.applyResultsOfAnalyzedLambdaToCandidateSystem - val dangerousBuilderInferenceWithoutAnnotation = - lambdaArguments.size >= 2 && lambdaArguments.count { it.notFixedInputTypeVariables().isNotEmpty() } >= 2 - - // We assume useBuilderInferenceWithoutAnnotation = true for FIR - - val builder = getBuilder() + var anyAnalyzed = false for (argument in lambdaArguments) { - val reallyHasBuilderInferenceAnnotation = argument.isCorrespondingParameterAnnotatedWithBuilderInference - val notFixedInputTypeVariables = argument.notFixedInputTypeVariables() if (notFixedInputTypeVariables.isEmpty()) continue - - // we have dangerous inference situation - // if lambda annotated with BuilderInference it's probably safe, due to type shape - // otherwise report multi-lambda builder inference restriction diagnostic - if (checkForDangerousBuilderInference && dangerousBuilderInferenceWithoutAnnotation && !reallyHasBuilderInferenceAnnotation) { - for (variable in notFixedInputTypeVariables) { - addError(AnonymousFunctionBasedMultiLambdaBuilderInferenceRestriction(argument.atom, variable.typeParameter!!)) - } - } - - for (variable in notFixedInputTypeVariables) { - builder.markPostponedVariable(notFixedTypeVariables.getValue(variable).typeVariable) - } - analyze(argument) + + anyAnalyzed = true } - val variableForFixation = variableFixationFinder.findFirstVariableForFixation( - this, allTypeVariables, postponedArguments, completionMode, topLevelType - ) - - // continue completion (rerun stages) only if ready for fixation variables with proper constraints have appeared - // (after analysing a lambda with the builder inference) - // otherwise we don't continue and report "not enough type information" error - return variableForFixation?.hasProperConstraint == true + return anyAnalyzed } private fun transformToAtomWithNewFunctionExpectedType( @@ -277,16 +288,12 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c collectVariablesFromContext: Boolean, postponedArguments: List, ): Boolean { - val variableForFixation = variableFixationFinder.findFirstVariableForFixation( - this, - getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms), - postponedArguments, - completionMode, - topLevelType + val variableForFixation = findFirstVariableForFixation( + collectVariablesFromContext, topLevelAtoms, postponedArguments, completionMode, topLevelType ) ?: return false val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) - if (!variableForFixation.hasProperConstraint) return false + if (!variableForFixation.isReady) return false fixVariable(this, variableWithConstraints) @@ -297,19 +304,17 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c completionMode: ConstraintSystemCompletionMode, topLevelAtoms: List, topLevelType: ConeKotlinType, - allTypeVariables: List, postponedArguments: List, ) { while (true) { - val variableForFixation = variableFixationFinder.findFirstVariableForFixation( - this, allTypeVariables, postponedArguments, completionMode, topLevelType, - ) ?: break - - assert(!variableForFixation.hasProperConstraint) { + val variableForFixation = + findFirstVariableForFixation(false, topLevelAtoms, postponedArguments, completionMode, topLevelType) + ?: break + assert(!variableForFixation.isReady) { "At this stage there should be no remaining variables with proper constraints" } - if (completionMode == ConstraintSystemCompletionMode.PARTIAL) break + if (completionMode == ConstraintSystemCompletionMode.PARTIAL || completionMode == ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL) break val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) processVariableWhenNotEnoughInformation(variableWithConstraints, topLevelAtoms) @@ -399,30 +404,9 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c topLevelAtom.collectAllTypeVariables() } - checkNotFixedTypeVariablesCountConsistency(result) - return result.toList() } - private fun ConstraintSystemCompletionContext.checkNotFixedTypeVariablesCountConsistency( - result: LinkedHashSet, - ) { - val notFixedTypeVariablesToUse = - when (outerSystemVariablesPrefixSize) { - 0 -> notFixedTypeVariables.keys - else -> notFixedTypeVariables.keys.toMutableSet().apply { - removeAll(allTypeVariables.keys.take(outerSystemVariablesPrefixSize).toSet()) - } - } - - require(result.size == notFixedTypeVariablesToUse.size) { - val notFoundTypeVariables = notFixedTypeVariablesToUse.toMutableSet().apply { - removeAll(result) - } - "Not all type variables found: $notFoundTypeVariables" - } - } - private fun fixVariable( c: ConstraintSystemCompletionContext, variableWithConstraints: VariableWithConstraints, @@ -581,13 +565,21 @@ private fun FirResolvable.processCandidateIfApplicable( val candidate = (calleeReference as? FirNamedReferenceWithCandidate)?.candidate ?: return processor(candidate) + val visited = mutableSetOf() + for (atom in candidate.postponedAtoms) { if (atom !is ResolvedLambdaAtom || !atom.analyzed) continue atom.returnStatements.forEach { + visited += it it.processAllContainingCallCandidates(processBlocks, processor) } } + + for (call in candidate.postponedPCLACalls) { + if (!visited.add(call)) continue + call.processAllContainingCallCandidates(processBlocks, processor) + } } val Candidate.csBuilder: NewConstraintSystemImpl get() = system.getBuilder() 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 deleted file mode 100644 index 99ea27a8a4e..00000000000 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. - */ - -package org.jetbrains.kotlin.fir.resolve.inference - -import org.jetbrains.kotlin.config.LanguageFeature -import org.jetbrains.kotlin.fir.FirElement -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.* -import org.jetbrains.kotlin.fir.languageVersionSettings -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.inference.model.ConeBuilderInferenceSubstitutionConstraintPosition -import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor -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.types.* -import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer -import org.jetbrains.kotlin.fir.visitors.transformSingle -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder -import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor -import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode -import org.jetbrains.kotlin.resolve.calls.inference.model.* -import org.jetbrains.kotlin.resolve.calls.inference.registerTypeVariableIfNotPresent -import org.jetbrains.kotlin.resolve.descriptorUtil.BUILDER_INFERENCE_ANNOTATION_FQ_NAME -import org.jetbrains.kotlin.types.model.TypeConstructorMarker -import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker -import org.jetbrains.kotlin.types.model.safeSubstitute - -/** - * General documentation for builder inference algorithm is located at `/docs/fir/builder_inference.md` - */ -class FirBuilderInferenceSession( - private val lambda: FirAnonymousFunction, - resolutionContext: ResolutionContext, - private val stubsForPostponedVariables: Map, -) : FirInferenceSessionForChainedResolve(resolutionContext) { - private val session = resolutionContext.session - private val commonCalls: MutableList> = mutableListOf() - private var lambdaImplicitReceivers: MutableList = mutableListOf() - - override fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement { - val candidate = call.candidate - val system = candidate.system - - if (system.hasContradiction) return true - if (!candidate.isSuitableForBuilderInference()) return true - - - val storage = system.getBuilder().currentStorage() - - if (call.hasPostponed()) return true - - return storage.notFixedTypeVariables.keys.all { - val variable = storage.allTypeVariables[it] - val isPostponed = variable != null && variable in storage.postponedTypeVariables - isPostponed || components.callCompleter.completer.variableFixationFinder.isTypeVariableHasProperConstraint(system, it) - } - } - - private fun Candidate.isSuitableForBuilderInference(): Boolean { - val extensionReceiver = chosenExtensionReceiver - val dispatchReceiver = dispatchReceiver - return when { - extensionReceiver == null && dispatchReceiver == null -> false - dispatchReceiver?.resolvedType?.containsStubType() == true -> true - extensionReceiver?.resolvedType?.containsStubType() == true -> symbol.fir.hasBuilderInferenceAnnotation(session) - else -> false - } - } - - private fun ConeKotlinType.containsStubType(): Boolean { - return this.contains { - it is ConeStubTypeForChainInference - } - } - - private fun FirStatement.hasPostponed(): Boolean { - var result = false - processAllContainingCallCandidates(processBlocks = false) { - result = result || it.hasPostponed() - } - return result - } - - private fun Candidate.hasPostponed(): Boolean { - return postponedAtoms.any { !it.analyzed } - } - - fun addLambdaImplicitReceiver(receiver: ImplicitExtensionReceiverValue) { - lambdaImplicitReceivers += receiver - } - - override fun addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement { - if (skipCall(call)) return - commonCalls += call to candidate - } - - @Suppress("UNUSED_PARAMETER") - private fun skipCall(call: T): Boolean where T : FirResolvable, T : FirStatement { - // TODO: what is FIR analog? - // if (descriptor is FakeCallableDescriptorForObject) return true - // if (!DescriptorUtils.isObject(descriptor) && isInLHSOfDoubleColonExpression(callInfo)) return true - - return false - } - - override fun inferPostponedVariables( - lambda: ResolvedLambdaAtom, - constraintSystemBuilder: ConstraintSystemBuilder, - completionMode: ConstraintSystemCompletionMode, - candidate: Candidate - ): Map? { - val (commonSystem, effectivelyEmptyConstraintSystem) = buildCommonSystem(constraintSystemBuilder.currentStorage()) - val resultingSubstitutor by lazy { getResultingSubstitutor(commonSystem) } - - if (effectivelyEmptyConstraintSystem) { - updateCalls(resultingSubstitutor) - return null - } - - val context = commonSystem.asConstraintSystemCompleterContext() - components.callCompleter.completer.complete( - context, - completionMode, - partiallyResolvedCalls.map { it.first as FirStatement }, - components.session.builtinTypes.unitType.type, resolutionContext, - collectVariablesFromContext = true - ) { - error("Shouldn't be called in complete constraint system mode") - } - - if (completionMode == ConstraintSystemCompletionMode.FULL) { - constraintSystemBuilder.substituteFixedVariables(resultingSubstitutor) - } - - if (!session.languageVersionSettings.supportsFeature(LanguageFeature.NoAdditionalErrorsInK1DiagnosticReporter)) { - for (error in commonSystem.errors) { - candidate.system.addError(error) - } - } - updateCalls(resultingSubstitutor) - - @Suppress("UNCHECKED_CAST") - return commonSystem.fixedTypeVariables as Map - } - - private fun buildCommonSystem(initialStorage: ConstraintStorage): Pair { - // TODO(KT-64034): Missing handling of parent builder inference sessions - // - See [org.jetbrains.kotlin.resolve.calls.inference.BuilderInferenceSession.initializeCommonSystem] - - val commonSystem = components.session.inferenceComponents.createConstraintSystem() - val nonFixedToVariablesSubstitutor = createNonFixedTypeToVariableSubstitutor() - - var effectivelyEmptyCommonSystem = - !integrateConstraints(commonSystem, initialStorage, nonFixedToVariablesSubstitutor, false) - - for ((_, candidate) in commonCalls) { - val hasConstraints = - integrateConstraints(commonSystem, candidate.system.asReadOnlyStorage(), nonFixedToVariablesSubstitutor, false) - if (hasConstraints) effectivelyEmptyCommonSystem = false - } - for ((_, candidate) in partiallyResolvedCalls) { - val hasConstraints = - integrateConstraints(commonSystem, candidate.system.asReadOnlyStorage(), nonFixedToVariablesSubstitutor, true) - if (hasConstraints) effectivelyEmptyCommonSystem = false - } - - return commonSystem to effectivelyEmptyCommonSystem - } - - private fun createNonFixedTypeToVariableSubstitutor(): ConeSubstitutor { - val typeContext = components.session.typeContext - - val bindings = mutableMapOf() - for ((variable, nonFixedType) in stubsForPostponedVariables) { - bindings[nonFixedType.constructor] = variable.defaultType - } - - return typeContext.typeSubstitutorByTypeConstructor(bindings) - } - - private fun integrateConstraints( - commonSystem: NewConstraintSystemImpl, - storage: ConstraintStorage, - nonFixedToVariablesSubstitutor: ConeSubstitutor, - shouldIntegrateAllConstraints: Boolean - ): Boolean { - storage.notFixedTypeVariables.values.forEach { commonSystem.registerTypeVariableIfNotPresent(it.typeVariable) } - - /* - * storage can contain the following substitutions: - * TypeVariable(A) -> ProperType - * TypeVariable(B) -> Special-Non-Fixed-Type - * - * while substitutor from parameter map non-fixed types to the original type variable - * */ - val callSubstitutor = - storage.buildAbstractResultingSubstitutor(commonSystem, transformTypeVariablesToErrorTypes = false) as ConeSubstitutor - - var introducedConstraint = false - - for (initialConstraint in storage.initialConstraints) { - if (initialConstraint.position is BuilderInferencePosition) continue - if (integrateConstraintToSystem( - commonSystem, initialConstraint, callSubstitutor, nonFixedToVariablesSubstitutor - ) - ) { - introducedConstraint = true - } - } - - if (shouldIntegrateAllConstraints) { - for ((variableConstructor, type) in storage.fixedTypeVariables) { - val typeVariable = storage.allTypeVariables.getValue(variableConstructor) - commonSystem.registerTypeVariableIfNotPresent(typeVariable) - commonSystem.addEqualityConstraint((typeVariable as ConeTypeVariable).defaultType, type, BuilderInferencePosition) - introducedConstraint = true - } - } - - return introducedConstraint - } - - private fun extractCommonCapturedTypes(lower: ConeKotlinType, upper: ConeKotlinType): List { - val extractedCapturedTypes = mutableSetOf().also { extractCapturedTypesTo(lower, it) } - return extractedCapturedTypes.filter { capturedType -> - upper.contains { it is ConeCapturedType && it.constructor === capturedType.constructor } - } - } - - private fun extractCapturedTypesTo(type: ConeKotlinType, to: MutableSet) { - if (type is ConeCapturedType) { - to.add(type) - } - for (typeArgument in type.typeArguments) { - if (typeArgument !is ConeKotlinTypeProjection) continue - extractCapturedTypesTo(typeArgument.type, to) - } - } - - private fun getResultingSubstitutor(commonSystem: NewConstraintSystemImpl): ConeSubstitutor { - val nonFixedToVariablesSubstitutor = createNonFixedTypeToVariableSubstitutor() - val commonSystemSubstitutor = commonSystem.buildCurrentSubstitutor() as ConeSubstitutor - return ChainedSubstitutor(nonFixedToVariablesSubstitutor, commonSystemSubstitutor) - .replaceStubsAndTypeVariablesToErrors(resolutionContext.typeContext, stubsForPostponedVariables.values.map { it.constructor }) - } - - private fun updateCalls(substitutor: ConeSubstitutor) { - val stubTypeSubstitutor = FirStubTypeTransformer(substitutor) - lambda.transformSingle(stubTypeSubstitutor, null) - - // TODO: Builder inference should not modify implicit receivers. KT-54708 - for (receiver in lambdaImplicitReceivers) { - @Suppress("DEPRECATION_ERROR") - receiver.updateTypeInBuilderInference(substitutor.substituteOrSelf(receiver.type)) - } - - val completionResultsWriter = components.callCompleter.createCompletionResultsWriter(substitutor) - for ((call, _) in partiallyResolvedCalls) { - call.transformSingle(completionResultsWriter, null) - } - } - - private fun integrateConstraintToSystem( - commonSystem: NewConstraintSystemImpl, - initialConstraint: InitialConstraint, - callSubstitutor: ConeSubstitutor, - nonFixedToVariablesSubstitutor: ConeSubstitutor, - ): Boolean { - val substitutedConstraintWith = - initialConstraint.substitute(callSubstitutor).substituteNonFixedToVariables(nonFixedToVariablesSubstitutor) - - val substitutedA = substitutedConstraintWith.a - val substitutedB = substitutedConstraintWith.b - - if (commonSystem.isProperType(substitutedA) && (substitutedA == substitutedB || commonSystem.isProperType(substitutedB))) return false - - val position = substitutedConstraintWith.position - when (initialConstraint.constraintKind) { - ConstraintKind.LOWER -> error("LOWER constraint shouldn't be used, please use UPPER") - - ConstraintKind.UPPER -> commonSystem.addSubtypeConstraint(substitutedA, substitutedB, position) - - ConstraintKind.EQUALITY -> - with(commonSystem) { - addSubtypeConstraint(substitutedA, substitutedB, position) - addSubtypeConstraint(substitutedB, substitutedA, position) - } - } - return true - } - - private fun InitialConstraint.substitute(substitutor: TypeSubstitutorMarker): InitialConstraint { - val substitutedA = substitutor.safeSubstitute(resolutionContext.typeContext, this.a) - val substitutedB = substitutor.safeSubstitute(resolutionContext.typeContext, this.b) - - if (substitutedA == a && substitutedB == b) return this - - // TODO(KT-64033): Missing check for ForbidInferringPostponedTypeVariableIntoDeclaredUpperBound language feature - - return InitialConstraint( - substitutedA, - substitutedB, - this.constraintKind, - ConeBuilderInferenceSubstitutionConstraintPosition(this) - ) - } - - /** - * This function substitutes stub types in constraint with the corresponding type variable - * Please make sure to pass correct {Stub(Tv) => Tv} substitutor - * - * E.g.: - * ``` - * nonFixedToVariablesSubstitutor = {Stub(Tv) => Tv, Stub(Ov) => Ov} - * constraint: A <: B -> A <: B - * ``` - * - * The main reason for this function's existence is the custom handling of captured types. - * See KT-64027 - * - * @return Constraint, substituted according to the [nonFixedToVariablesSubstitutor] - */ - private fun InitialConstraint.substituteNonFixedToVariables( - nonFixedToVariablesSubstitutor: ConeSubstitutor - ): InitialConstraint { - require(constraintKind != ConstraintKind.LOWER) - - // TODO(KT-63996, KT-64027): This function assumes types passed in lower, upper order which isn't true for equality constraints - val commonCapTypes = extractCommonCapturedTypes(lower = a as ConeKotlinType, upper = b as ConeKotlinType) - - // TODO(KT-64027): This logic tries to work-around the problem with type substitution consistency in captured types - // In order to preserve consistency we collect captured types from both a and b and substitute them collectively - // E.g: - // substitutor = { B => W } - // a = C, b = D - // commonCapTypes = [CapturedType(out B)_0] - // capTypesSubstitutor = { CapturedTypeConstructor_0 => CapturedType(out W)_1 } - // substitutedA = C - // substitutedB = D - val substitutedCommonCapType = commonCapTypes.associate { - it.constructor to nonFixedToVariablesSubstitutor.substituteOrSelf(it) - } - - val capTypesSubstitutor = object : AbstractConeSubstitutor(resolutionContext.typeContext) { - override fun substituteType(type: ConeKotlinType): ConeKotlinType? { - if (type !is ConeCapturedType) return null - return substitutedCommonCapType[type.constructor] - } - } - - val substitutedA = nonFixedToVariablesSubstitutor.safeSubstitute( - resolutionContext.typeContext, - capTypesSubstitutor.safeSubstitute(resolutionContext.typeContext, this.a) - ) - val substitutedB = nonFixedToVariablesSubstitutor.safeSubstitute( - resolutionContext.typeContext, - capTypesSubstitutor.safeSubstitute(resolutionContext.typeContext, this.b) - ) - - return InitialConstraint( - substitutedA, - substitutedB, - constraintKind, - position - ) - } -} - -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) } - } - - // Since FirExpressions don't have typeRefs, they need to be updated separately. - // FirAnonymousFunctionExpression doesn't support replacing the type - // since it delegates the getter to the underlying FirAnonymousFunction. - if (element is FirExpression && element !is FirAnonymousFunctionExpression) { - // TODO Check why some expressions have unresolved type in builder inference session KT-61835 - @OptIn(UnresolvedExpressionTypeAccess::class) - element.coneTypeOrNull - ?.let(substitutor::substituteOrNull) - ?.let { element.replaceConeTypeOrNull(it) } - } - - @Suppress("UNCHECKED_CAST") - return element.transformChildren(this, data = null) as E - } - - override fun transformTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: Nothing?): FirStatement { - if (typeOperatorCall.argument.resolvedType is ConeStubType) { - typeOperatorCall.replaceArgFromStubType(true) - } - return super.transformTypeOperatorCall(typeOperatorCall, data) - } - - 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) - -fun FirDeclaration.hasBuilderInferenceAnnotation(session: FirSession): Boolean = - hasAnnotation(BUILDER_INFERENCE_ANNOTATION_CLASS_ID, session) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt index 7b2489d04ee..5b8384f1401 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt @@ -40,9 +40,8 @@ import org.jetbrains.kotlin.resolve.calls.inference.addEqualityConstraintIfCompa import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.types.TypeApproximatorConfiguration -import org.jetbrains.kotlin.types.model.StubTypeMarker -import org.jetbrains.kotlin.types.model.TypeVariableMarker import org.jetbrains.kotlin.types.model.safeSubstitute import org.jetbrains.kotlin.utils.addToStdlib.runIf @@ -80,18 +79,11 @@ class FirCallCompleter( val completionMode = candidate.computeCompletionMode( session.inferenceComponents, resolutionMode, initialType ).let { - // The difference between `shouldAvoidFullCompletion` and `!shouldRunCompletion` is very subtle: - // we don't run even partial completion for `!inferenceSession.shouldRunCompletion(call)`, while actually - // do that for `shouldAvoidFullCompletion` - // - // As for implementations, `shouldRunCompletion` only works for Builder inference, while `shouldAvoidFullCompletion` is for - // delegate inference where it's assumed to have partially completed intermediate calls. - // - // Ideally, we should get rid of `shouldRunCompletion` once Builder inference is rewritten (see KT-61041 for tracking) - if (it == ConstraintSystemCompletionMode.FULL && inferenceSession.shouldAvoidFullCompletion(call)) - ConstraintSystemCompletionMode.PARTIAL - else - it + when { + it == ConstraintSystemCompletionMode.FULL -> + inferenceSession.customCompletionModeInsteadOfFull(call) ?: ConstraintSystemCompletionMode.FULL + else -> it + } } val analyzer = createPostponedArgumentsAnalyzer(transformer.resolutionContext) @@ -101,35 +93,27 @@ class FirCallCompleter( return when (completionMode) { ConstraintSystemCompletionMode.FULL -> { - if (inferenceSession.shouldRunCompletion(call)) { - runCompletionForCall(candidate, completionMode, call, initialType, analyzer) - val finalSubstitutor = candidate.system.asReadOnlyStorage() - .buildAbstractResultingSubstitutor(session.typeContext) as ConeSubstitutor - val completedCall = call.transformSingle( - FirCallCompletionResultsWriterTransformer( - session, components.scopeSession, finalSubstitutor, - components.returnTypeCalculator, - session.typeApproximator, - components.dataFlowAnalyzer, - components.integerLiteralAndOperatorApproximationTransformer, - components.samResolver, - components.context, - ), - null - ) - inferenceSession.addCompletedCall(completedCall, candidate) - completedCall - } else { - inferenceSession.processPartiallyResolvedCall(call, resolutionMode) - call - } + runCompletionForCall(candidate, completionMode, call, initialType, analyzer) + val finalSubstitutor = candidate.system.asReadOnlyStorage() + .buildAbstractResultingSubstitutor(session.typeContext) as ConeSubstitutor + call.transformSingle( + FirCallCompletionResultsWriterTransformer( + session, components.scopeSession, finalSubstitutor, + components.returnTypeCalculator, + session.typeApproximator, + components.dataFlowAnalyzer, + components.integerLiteralAndOperatorApproximationTransformer, + components.samResolver, + components.context, + ), + null + ) } - ConstraintSystemCompletionMode.PARTIAL -> { + ConstraintSystemCompletionMode.PARTIAL, ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL -> { runCompletionForCall(candidate, completionMode, call, initialType, analyzer) - if (inferenceSession is FirDelegatedPropertyInferenceSession) { - inferenceSession.processPartiallyResolvedCall(call, resolutionMode) - } + + inferenceSession.processPartiallyResolvedCall(call, resolutionMode, completionMode) call } @@ -183,8 +167,8 @@ class FirCallCompleter( completionMode: ConstraintSystemCompletionMode, call: T, initialType: ConeKotlinType, - analyzer: PostponedArgumentsAnalyzer? = null - ) where T : FirResolvable, T : FirStatement { + analyzer: PostponedArgumentsAnalyzer? = null, + ) where T : FirStatement { @Suppress("NAME_SHADOWING") val analyzer = analyzer ?: createPostponedArgumentsAnalyzer(transformer.resolutionContext) completer.complete( @@ -194,7 +178,7 @@ class FirCallCompleter( initialType, transformer.resolutionContext ) { - analyzer.analyze(candidate.system, it, candidate, completionMode) + analyzer.analyze(candidate.system, it, candidate) } } @@ -251,8 +235,9 @@ class FirCallCompleter( contextReceivers: List, parameters: List, expectedReturnType: ConeKotlinType?, - stubsForPostponedVariables: Map, - candidate: Candidate + candidate: Candidate, + withPCLASession: Boolean, + forOverloadByLambdaReturnType: Boolean, ): ReturnArgumentsAnalysisResult { val lambdaArgument: FirAnonymousFunction = lambdaAtom.atom val needItParam = lambdaArgument.valueParameters.isEmpty() && parameters.size == 1 @@ -355,29 +340,34 @@ class FirCallCompleter( } ?: components.noExpectedType ) - val builderInferenceSession = runIf(stubsForPostponedVariables.isNotEmpty()) { - @Suppress("UNCHECKED_CAST") - FirBuilderInferenceSession( - lambdaArgument, - transformer.resolutionContext, - stubsForPostponedVariables as Map - ) - } + var additionalConstraints: ConstraintStorage? = null transformer.context.withAnonymousFunctionTowerDataContext(lambdaArgument.symbol) { - if (builderInferenceSession != null) { - transformer.context.withInferenceSession(builderInferenceSession) { + val pclaInferenceSession = + runIf(withPCLASession) { + candidate.lambdasAnalyzedWithPCLA += lambdaArgument + + FirPCLAInferenceSession(candidate, session.inferenceComponents, transformer.context.returnTypeCalculator) + } + + if (pclaInferenceSession != null) { + transformer.context.withInferenceSession(pclaInferenceSession) { lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef)) + + applyResultsToMainCandidate() } } else { - lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef)) + additionalConstraints = + transformer.context.inferenceSession.runLambdaCompletion(candidate, forOverloadByLambdaReturnType) { + lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef)) + } } } transformer.context.dropContextForAnonymousFunction(lambdaArgument) val returnArguments = components.dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(lambdaArgument).map { it.expression } - return ReturnArgumentsAnalysisResult(returnArguments, builderInferenceSession) + return ReturnArgumentsAnalysisResult(returnArguments, additionalConstraints) } } @@ -385,7 +375,7 @@ class FirCallCompleter( // We only run lambda completion from ConstraintSystemCompletionContext.analyzeRemainingNotAnalyzedPostponedArgument when they are // left uninferred. // Currently, we use stub types for builder inference, so CANNOT_INFER_PARAMETER_TYPE is the only possible result here. - if (this is ConeTypeVariableType) { + if (isTypeVariableThatShouldBeFixedBeforeLambdaAnalysis(isReceiver = valueParameter == null)) { val diagnostic = valueParameter?.let(::ConeCannotInferValueParameterType) ?: ConeCannotInferReceiverParameterType() return ConeErrorType(diagnostic) } @@ -394,6 +384,20 @@ class FirCallCompleter( this, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference ) ?: this } + + private fun ConeKotlinType.isTypeVariableThatShouldBeFixedBeforeLambdaAnalysis(isReceiver: Boolean): Boolean { + if (this !is ConeTypeVariableType) return false + + // Outside PCLA, all type variables for parameter types should be fixed before lambda analysis + // Inside PCLA, we force fixing receivers before lambda analysis + if (inferenceSession !is FirPCLAInferenceSession || isReceiver) return true + + // For type variables not based on type parameters (created for lambda parameters with no expected type) + // we force them to be fixed before lambda analysis + if (typeConstructor.originalTypeParameter == null) return true + + return false + } } private fun Candidate.isFunctionForExpectTypeFromCastFeature(): Boolean { 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 4e5283f7dc0..b71e994a4f4 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 @@ -5,55 +5,72 @@ package org.jetbrains.kotlin.fir.resolve.inference -import org.jetbrains.kotlin.fir.declarations.FirProperty -import org.jetbrains.kotlin.fir.expressions.FirFunctionCall -import org.jetbrains.kotlin.fir.expressions.FirFunctionCallOrigin -import org.jetbrains.kotlin.fir.expressions.FirResolvable -import org.jetbrains.kotlin.fir.expressions.FirStatement +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.render +import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.resolve.calls.InferenceError import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext import org.jetbrains.kotlin.fir.resolve.calls.candidate +import org.jetbrains.kotlin.fir.resolve.initialTypeOfCandidate import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor +import org.jetbrains.kotlin.fir.resolve.transformers.FirCallCompletionResultsWriterTransformer import org.jetbrains.kotlin.fir.types.ConeClassLikeType -import org.jetbrains.kotlin.fir.types.ConeKotlinType -import org.jetbrains.kotlin.fir.types.ConeTypeVariableTypeConstructor import org.jetbrains.kotlin.fir.types.typeContext -import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder +import org.jetbrains.kotlin.fir.visitors.transformSingle import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.util.OperatorNameConventions +import org.jetbrains.kotlin.utils.addIfNotNull class FirDelegatedPropertyInferenceSession( - val property: FirProperty, - resolutionContext: ResolutionContext, - private val postponedArgumentsAnalyzer: PostponedArgumentsAnalyzer, -) : FirInferenceSessionForChainedResolve(resolutionContext) { + private val resolutionContext: ResolutionContext, + private val callCompleter: FirCallCompleter, + private val delegateExpression: FirExpression, +) : FirInferenceSession() { + + private val partiallyResolvedCalls: MutableList> = mutableListOf() + + private val components: BodyResolveComponents + get() = resolutionContext.bodyResolveComponents + + private val FirResolvable.candidate: Candidate + get() = candidate()!! + + private val nonTrivialParentSession: FirInferenceSession? = + resolutionContext.bodyResolveContext.inferenceSession.takeIf { it !== DEFAULT } + + private val delegateCandidate = (delegateExpression as? FirResolvable)?.candidate() + private val parentConstraintSystem = + delegateCandidate?.system + ?: (resolutionContext.bodyResolveContext.inferenceSession as? FirPCLAInferenceSession)?.currentCommonSystem + ?: components.session.inferenceComponents.createConstraintSystem() + + private val currentConstraintSystem = + prepareSharedBaseSystem(parentConstraintSystem, components.session.inferenceComponents) - private var currentConstraintSystem = components.session.inferenceComponents.createConstraintSystem() val currentConstraintStorage: ConstraintStorage get() = currentConstraintSystem.currentStorage() private val unitType: ConeClassLikeType = components.session.builtinTypes.unitType.type - // TODO after PCLA (KT-59107): - // Outer system seems to be a property of a concrete call resolution and probably should be applied to concrete call resolution - override fun onCandidatesResolution(call: FirFunctionCall, candidatesResolutionCallback: () -> R): R { - return if (!call.isAnyOfDelegateOperators()) - candidatesResolutionCallback() - else - resolutionContext.bodyResolveContext.withOuterConstraintStorage( - currentConstraintSystem.currentStorage(), - candidatesResolutionCallback - ) + private var wasCompletionRun = false + + override fun customCompletionModeInsteadOfFull(call: FirResolvable): ConstraintSystemCompletionMode? = when { + call.isAnyOfDelegateOperators() && !wasCompletionRun -> ConstraintSystemCompletionMode.PARTIAL + else -> null } - override fun shouldAvoidFullCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = - call.isAnyOfDelegateOperators() + override fun processPartiallyResolvedCall( + call: T, + resolutionMode: ResolutionMode, + completionMode: ConstraintSystemCompletionMode, + ) where T : FirResolvable, T : FirStatement { + if (wasCompletionRun || !call.isAnyOfDelegateOperators()) return - override fun processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement { - if (resolutionMode != ResolutionMode.ContextDependent.Delegate && !call.isAnyOfDelegateOperators()) return + requireCallIsDelegateOperator(call) val candidate = call.candidate @@ -64,38 +81,84 @@ class FirDelegatedPropertyInferenceSession( val candidateSystem = candidate.system partiallyResolvedCalls.add(call to candidate) - currentConstraintSystem = candidateSystem + currentConstraintSystem.addOtherSystem(candidateSystem.currentStorage()) + } + + private fun requireCallIsDelegateOperator(call: T) where T : FirResolvable, T : FirStatement { + require(call.isAnyOfDelegateOperators()) { + "Unexpected ${call.render()} call" + } } private fun T.isProvideDelegate() where T : FirResolvable, T : FirStatement = - isAnyOfDelegateOperators() && (this as FirFunctionCall).calleeReference.name == OperatorNameConventions.PROVIDE_DELEGATE + isAnyOfDelegateOperators() && (this as FirResolvable).candidate()?.callInfo?.name == OperatorNameConventions.PROVIDE_DELEGATE - private fun T.isAnyOfDelegateOperators(): Boolean where T : FirResolvable { - if (this !is FirFunctionCall || origin != FirFunctionCallOrigin.Operator) return false - val name = calleeReference.name - return name == OperatorNameConventions.PROVIDE_DELEGATE || name == OperatorNameConventions.GET_VALUE || name == OperatorNameConventions.SET_VALUE + override fun baseConstraintStorageForCandidate(candidate: Candidate): ConstraintStorage? { + if (wasCompletionRun || !candidate.callInfo.callSite.isAnyOfDelegateOperators()) return null + return currentConstraintStorage } - override fun inferPostponedVariables( - lambda: ResolvedLambdaAtom, - constraintSystemBuilder: ConstraintSystemBuilder, - completionMode: ConstraintSystemCompletionMode, - candidate: Candidate - ): Map? = null + fun completeSessionOrPostponeIfNonRoot(onCompletionResultsWriting: (ConeSubstitutor) -> Unit) { + check(!wasCompletionRun) + wasCompletionRun = true - fun completeCandidates(): List { - val commonSystem = currentConstraintSystem.apply { prepareForGlobalCompletion() } + parentConstraintSystem.addOtherSystem(currentConstraintStorage) - val notCompletedCalls = partiallyResolvedCalls.mapNotNull { partiallyResolvedCall -> - partiallyResolvedCall.first.takeIf { resolvable -> - resolvable.candidate() != null + (nonTrivialParentSession as? FirPCLAInferenceSession)?.apply { + if (delegateCandidate != null) { + callCompleter.runCompletionForCall( + delegateCandidate, + ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL, + delegateExpression, + components.initialTypeOfCandidate(delegateCandidate) + ) } + + integrateChildSession( + buildList { + addIfNotNull(delegateExpression) + partiallyResolvedCalls.mapTo(this) { it.first as FirStatement } + }, + parentConstraintSystem.currentStorage(), + onCompletionResultsWriting, + ) + return } + val completedCalls = completeCandidatesForRootSession() + + val finalSubstitutor = parentConstraintSystem.asReadOnlyStorage() + .buildAbstractResultingSubstitutor(components.session.typeContext) as ConeSubstitutor + + val callCompletionResultsWriter = callCompleter.createCompletionResultsWriter( + finalSubstitutor, + // TODO: Get rid of the mode + mode = FirCallCompletionResultsWriterTransformer.Mode.DelegatedPropertyCompletion + ) + completedCalls.forEach { + it.transformSingle(callCompletionResultsWriter, null) + } + + onCompletionResultsWriting(finalSubstitutor) + } + + private fun completeCandidatesForRootSession(): List { + val parentSystem = parentConstraintSystem.apply { prepareForGlobalCompletion() } + + val notCompletedCalls = + buildList { + addIfNotNull(delegateExpression as? FirResolvable) + partiallyResolvedCalls.mapNotNullTo(this) { partiallyResolvedCall -> + partiallyResolvedCall.first.takeIf { resolvable -> + resolvable.candidate() != null + } + } + } + resolutionContext.bodyResolveContext.withInferenceSession(DEFAULT) { @Suppress("UNCHECKED_CAST") components.callCompleter.completer.complete( - commonSystem.asConstraintSystemCompleterContext(), + parentSystem.asConstraintSystemCompleterContext(), ConstraintSystemCompletionMode.FULL, notCompletedCalls as List, unitType, resolutionContext @@ -110,29 +173,31 @@ class FirDelegatedPropertyInferenceSession( } found }.candidate - postponedArgumentsAnalyzer.analyze( - commonSystem, + callCompleter.createPostponedArgumentsAnalyzer(resolutionContext).analyze( + parentSystem, lambdaAtom, containingCandidateForLambda, - ConstraintSystemCompletionMode.FULL, ) } } - for ((_, candidate) in partiallyResolvedCalls) { - for (error in commonSystem.errors) { + for (candidate in notCompletedCalls.mapNotNull { it.candidate() }) { + for (error in parentSystem.errors) { candidate.addDiagnostic(InferenceError(error)) } } return notCompletedCalls } - - fun createFinalSubstitutor(): ConeSubstitutor = - currentConstraintSystem.asReadOnlyStorage() - .buildAbstractResultingSubstitutor(components.session.typeContext) as ConeSubstitutor - - override fun addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {} - - override fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true +} + +fun FirElement.isAnyOfDelegateOperators(): Boolean { + if (this is FirPropertyAccessExpression) { + val originalCall = this.candidate()?.callInfo?.callSite as? FirFunctionCall ?: return false + return originalCall.isAnyOfDelegateOperators() + } + + if (this !is FirFunctionCall || origin != FirFunctionCallOrigin.Operator) return false + val name = calleeReference.name + return name == OperatorNameConventions.PROVIDE_DELEGATE || name == OperatorNameConventions.GET_VALUE || name == OperatorNameConventions.SET_VALUE } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt index 73b41195e54..ba2ba83d0f3 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt @@ -5,52 +5,57 @@ package org.jetbrains.kotlin.fir.resolve.inference -import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.expressions.FirResolvable import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.types.ConeKotlinType -import org.jetbrains.kotlin.fir.types.ConeTypeVariableTypeConstructor -import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage +import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl abstract class FirInferenceSession { companion object { - val DEFAULT: FirInferenceSession = object : FirStubInferenceSession() {} + val DEFAULT: FirInferenceSession = object : FirInferenceSession() { + override fun processPartiallyResolvedCall( + call: T, + resolutionMode: ResolutionMode, + completionMode: ConstraintSystemCompletionMode, + ) where T : FirResolvable, T : FirStatement { + // Do nothing + } + } + + @JvmStatic + protected fun prepareSharedBaseSystem( + outerSystem: NewConstraintSystemImpl, + components: InferenceComponents, + ): NewConstraintSystemImpl { + return components.createConstraintSystem().apply { + addOuterSystem(outerSystem.currentStorage()) + } + } } - abstract fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement + open fun runLambdaCompletion(candidate: Candidate, forOverloadByLambdaReturnType: Boolean, block: () -> Unit): ConstraintStorage? { + block() + return null + } - /** - * In some cases (like getValue/setValue resolution of property delegation convention), it might be necessary to postpone full completion - * even with the ContextIndependent mode (which is used for delegated accessors bodies) - */ - open fun shouldAvoidFullCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = false + open fun runCallableReferenceResolution(candidate: Candidate, block: () -> T): T = block() - abstract fun processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement - abstract fun addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement + open fun customCompletionModeInsteadOfFull( + call: FirResolvable, + ): ConstraintSystemCompletionMode? = null - abstract fun inferPostponedVariables( - lambda: ResolvedLambdaAtom, - constraintSystemBuilder: ConstraintSystemBuilder, - completionMode: ConstraintSystemCompletionMode, - candidate: Candidate - ): Map? + abstract fun processPartiallyResolvedCall( + call: T, + resolutionMode: ResolutionMode, + completionMode: ConstraintSystemCompletionMode + ) where T : FirResolvable, T : FirStatement - open fun onCandidatesResolution(call: FirFunctionCall, candidatesResolutionCallback: () -> R) = candidatesResolutionCallback() -} - -abstract class FirStubInferenceSession : FirInferenceSession() { - override fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true - - override fun processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement {} - override fun addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {} - - override fun inferPostponedVariables( - lambda: ResolvedLambdaAtom, - constraintSystemBuilder: ConstraintSystemBuilder, - completionMode: ConstraintSystemCompletionMode, - candidate: Candidate - ): Map? = null + open fun baseConstraintStorageForCandidate(candidate: Candidate): ConstraintStorage? = null + + open fun addSubtypeConstraintIfCompatible(lowerType: ConeKotlinType, upperType: ConeKotlinType, element: FirElement) {} } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSessionForChainedResolve.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSessionForChainedResolve.kt deleted file mode 100644 index 0df1f35d992..00000000000 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSessionForChainedResolve.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. - */ - -package org.jetbrains.kotlin.fir.resolve.inference - -import org.jetbrains.kotlin.fir.expressions.FirResolvable -import org.jetbrains.kotlin.fir.expressions.FirStatement -import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents -import org.jetbrains.kotlin.fir.resolve.ResolutionMode -import org.jetbrains.kotlin.fir.resolve.calls.Candidate -import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext -import org.jetbrains.kotlin.fir.resolve.calls.candidate - -abstract class FirInferenceSessionForChainedResolve( - protected val resolutionContext: ResolutionContext -) : FirInferenceSession() { - protected val partiallyResolvedCalls: MutableList> = mutableListOf() - - protected val components: BodyResolveComponents - get() = resolutionContext.bodyResolveComponents - - override fun addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement { - // do nothing - } - - override fun processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement { - partiallyResolvedCalls += call to call.candidate - } - - protected val FirResolvable.candidate: Candidate - get() = candidate()!! -} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirPCLAInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirPCLAInferenceSession.kt new file mode 100644 index 00000000000..9f781620b70 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirPCLAInferenceSession.kt @@ -0,0 +1,312 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.resolve.inference + +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.resolve.ResolutionMode +import org.jetbrains.kotlin.fir.resolve.calls.CallKind +import org.jetbrains.kotlin.fir.resolve.calls.Candidate +import org.jetbrains.kotlin.fir.resolve.calls.candidate +import org.jetbrains.kotlin.fir.resolve.calls.processConstraintStorageFromExpression +import org.jetbrains.kotlin.fir.resolve.inference.model.ConeExpectedTypeConstraintPosition +import org.jetbrains.kotlin.fir.resolve.inference.model.ConeFixVariableConstraintPosition +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor +import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator +import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer +import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible +import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode +import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage +import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl +import org.jetbrains.kotlin.types.model.TypeConstructorMarker +import org.jetbrains.kotlin.types.model.defaultType + + +class FirPCLAInferenceSession( + private val outerCandidate: Candidate, + private val inferenceComponents: InferenceComponents, + private val returnTypeCalculator: ReturnTypeCalculator, +) : FirInferenceSession() { + + var currentCommonSystem = prepareSharedBaseSystem(outerCandidate.system, inferenceComponents) + private set + + override fun baseConstraintStorageForCandidate(candidate: Candidate): ConstraintStorage? { + if (candidate.needsToBePostponed()) return currentCommonSystem.currentStorage() + return null + } + + override fun customCompletionModeInsteadOfFull( + call: FirResolvable, + ): ConstraintSystemCompletionMode? = when { + call.candidate()?.usedOuterCs == true -> ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL + else -> null + } + + override fun processPartiallyResolvedCall( + call: T, + resolutionMode: ResolutionMode, + completionMode: ConstraintSystemCompletionMode, + ) where T : FirResolvable, T : FirStatement { + if (call is FirExpression) { + call.updateReturnTypeWithCurrentSubstitutor(resolutionMode) + } + + val candidate = call.candidate() + if (candidate?.usedOuterCs != true) return + + // Integrating back would happen at FirDelegatedPropertyInferenceSession.completeSessionOrPostponeIfNonRoot + // after all other delegation-related calls are being analyzed + if (resolutionMode == ResolutionMode.ContextDependent.Delegate) return + + currentCommonSystem.replaceContentWith(candidate.system.currentStorage()) + + if (completionMode == ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL) { + outerCandidate.postponedPCLACalls += call + } + } + + override fun runLambdaCompletion(candidate: Candidate, forOverloadByLambdaReturnType: Boolean, block: () -> Unit): ConstraintStorage? { + if (forOverloadByLambdaReturnType) { + val constraintAccumulatorForLambda = + inferenceComponents.createConstraintSystem().apply { + setBaseSystem(currentCommonSystem.currentStorage()) + } + + runWithSpecifiedCurrentCommonSystem(constraintAccumulatorForLambda, block) + + return constraintAccumulatorForLambda.currentStorage() + } + + runWithSpecifiedCurrentCommonSystem(candidate.system, block) + + return null + } + + override fun runCallableReferenceResolution(candidate: Candidate, block: () -> T): T { + candidate.system.apply { + // It's necessary because otherwise when we create CS for a child, it would simplify constraints + // (see 3rd constructor of MutableVariableWithConstraints) + // and merging it back might become a problem for transaction logic because the latter literally remembers + // the number of constraints for each variable and then restores it back. + // But since the constraints are simplified in the child, their number might become even fewer, leading to incorrect behavior + // or runtime exceptions. + // See callableReferenceAsArgumentForTransaction.kt test data + notFixedTypeVariables.values.forEach { it.runConstraintsSimplification() } + } + return runWithSpecifiedCurrentCommonSystem(candidate.system, block) + } + + private fun runWithSpecifiedCurrentCommonSystem(newSystem: NewConstraintSystemImpl, block: () -> T): T { + val previous = currentCommonSystem + return try { + currentCommonSystem = newSystem + block() + } finally { + currentCommonSystem = previous + } + } + + fun applyResultsToMainCandidate() { + outerCandidate.system.replaceContentWith(currentCommonSystem.currentStorage()) + } + + fun integrateChildSession( + childCalls: Collection, + childStorage: ConstraintStorage, + onCompletionResultsWriting: (ConeSubstitutor) -> Unit, + ) { + outerCandidate.postponedPCLACalls += childCalls + currentCommonSystem.addOtherSystem(childStorage) + outerCandidate.onCompletionResultsWritingCallbacks += onCompletionResultsWriting + } + + private fun FirExpression.updateReturnTypeWithCurrentSubstitutor( + resolutionMode: ResolutionMode, + ) { + val additionalBindings = mutableMapOf() + val system = (this as? FirResolvable)?.candidate()?.system ?: currentCommonSystem + + if (resolutionMode is ResolutionMode.ReceiverResolution) { + fixVariablesForMemberScope(resolvedType, system)?.let { additionalBindings += it } + } + + val substitutor = system.buildCurrentSubstitutor(additionalBindings) as ConeSubstitutor + val updatedType = substitutor.substituteOrNull(resolvedType) + + if (updatedType != null) { + replaceConeTypeOrNull(updatedType) + } + } + + fun fixVariablesForMemberScope( + type: ConeKotlinType, + myCs: NewConstraintSystemImpl, + ): Pair? { + return when (type) { + is ConeFlexibleType -> fixVariablesForMemberScope(type.lowerBound, myCs) + is ConeDefinitelyNotNullType -> fixVariablesForMemberScope(type.original, myCs) + is ConeTypeVariableType -> fixVariablesForMemberScope(type, myCs) + else -> null + } + } + + private fun fixVariablesForMemberScope( + type: ConeTypeVariableType, + myCs: NewConstraintSystemImpl, + ): Pair? { + val coneTypeVariableTypeConstructor = type.typeConstructor + + require(coneTypeVariableTypeConstructor in myCs.allTypeVariables) { + "$coneTypeVariableTypeConstructor not found" + } + + if (coneTypeVariableTypeConstructor in myCs.outerTypeVariables.orEmpty()) return null + + val variableWithConstraints = myCs.notFixedTypeVariables[coneTypeVariableTypeConstructor] ?: return null + val c = myCs.getBuilder() + + val resultType = c.run { + c.withTypeVariablesThatAreCountedAsProperTypes(c.outerTypeVariables.orEmpty()) { + if (!inferenceComponents.variableFixationFinder.isTypeVariableHasProperConstraint(c, coneTypeVariableTypeConstructor)) { + return@withTypeVariablesThatAreCountedAsProperTypes null + } + inferenceComponents.resultTypeResolver.findResultType( + c, + variableWithConstraints, + TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN + ) as ConeKotlinType + } + } ?: return null + val variable = variableWithConstraints.typeVariable + // TODO: Consider using different position (KT-64860) + c.addEqualityConstraint(variable.defaultType(c), resultType, ConeFixVariableConstraintPosition(variable)) + + return Pair(coneTypeVariableTypeConstructor, resultType) + } + + private fun FirExpression.shouldBePostponed(): Boolean { + if (this is FirWrappedArgumentExpression) return expression.shouldBePostponed() + if (this is FirAnonymousFunctionExpression) return true + if (this is FirCallableReferenceAccess) return true + if (this is FirAnonymousObjectExpression) return true + return false + } + + private fun Candidate.needsToBePostponed(): Boolean { + if (dispatchReceiver?.isReceiverPostponed() == true) return true + if (givenExtensionReceiverOptions.any { it.isReceiverPostponed() }) return true + + if (callInfo.arguments.any { it.shouldBePostponed() }) return true + + if (callInfo.callKind == CallKind.VariableAccess) { + val returnType = (symbol as? FirVariableSymbol)?.let(returnTypeCalculator::tryCalculateReturnType) + if (returnType?.type?.containsNotFixedTypeVariables() == true) return true + } + if (callInfo.arguments.any { it.isQualifiedAccessContainingTypeVariables() || it.doesArgumentUseOuterCS() }) return true + + if (callInfo.resolutionMode is ResolutionMode.ContextDependent.Delegate) return true + + // For assignments like myVarContainingTV = SomeCallWithNonTrivialInference(...) + // We should integrate the call into the PCLA tree, too + if ((callInfo.resolutionMode as? ResolutionMode.WithExpectedType)?.expectedTypeRef?.type?.containsNotFixedTypeVariables() == true) { + return true + } + + // Synthetic calls with blocks work like lambdas + if (callInfo.callKind == CallKind.SyntheticSelect) return true + + return false + } + + private fun FirExpression.doesArgumentUseOuterCS(): Boolean { + var result = false + processConstraintStorageFromExpression(this) { + if (it.usesOuterCs) { + result = true + } + } + + return result + } + + private fun FirExpression.isReceiverPostponed(): Boolean { + if (resolvedType.containsNotFixedTypeVariables()) return true + if ((this as? FirResolvable)?.candidate()?.usedOuterCs == true) return true + + return false + } + + private fun FirExpression.isQualifiedAccessContainingTypeVariables(): Boolean { + if (this is FirNamedArgumentExpression) return expression.isQualifiedAccessContainingTypeVariables() + + if (!isQualifiedAccessOrSmartCastOnIt()) return false + if (this is FirCallableReferenceAccess) return false + + return resolvedType.containsNotFixedTypeVariables() && (this as? FirResolvable)?.candidate() == null + } + + private fun FirExpression.isQualifiedAccessOrSmartCastOnIt(): Boolean = when (this) { + is FirQualifiedAccessExpression -> true + is FirSmartCastExpression -> originalExpression.isQualifiedAccessOrSmartCastOnIt() + else -> false + } + + private fun ConeKotlinType.containsNotFixedTypeVariables(): Boolean = + contains { + // TODO: Investigate why using `notFixedTypeVariables` instead of `allTypeVariables` leads to failure of the test (KT-64861) + // org.jetbrains.kotlin.test.runners.codegen.FirPsiBlackBoxCodegenTestGenerated.BuilderInference.OneParameter.OneTypeVariable. + // OneTypeInfoOrigin.SourceSinkFeedContexts.testThroughDelegatedLocalVariableYieldCase + it is ConeTypeVariableType && it.typeConstructor in currentCommonSystem.allTypeVariables + } + + override fun addSubtypeConstraintIfCompatible(lowerType: ConeKotlinType, upperType: ConeKotlinType, element: FirElement) { + currentCommonSystem.addSubtypeConstraintIfCompatible(lowerType, upperType, ConeExpectedTypeConstraintPosition) + } +} + +class FirTypeVariablesAfterPCLATransformer(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) } + } + + // Since FirExpressions don't have typeRefs, they need to be updated separately. + // FirAnonymousFunctionExpression doesn't support replacing the type + // since it delegates the getter to the underlying FirAnonymousFunction. + if (element is FirExpression && element !is FirAnonymousFunctionExpression) { + // TODO Check why some expressions have unresolved type in builder inference session KT-61835 + @OptIn(UnresolvedExpressionTypeAccess::class) + element.coneTypeOrNull + ?.let(substitutor::substituteOrNull) + ?.let { element.replaceConeTypeOrNull(it) } + } + + @Suppress("UNCHECKED_CAST") + 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) } + } +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt index 2012422e9d7..605a5503e75 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt @@ -19,13 +19,16 @@ import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzerContext import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder -import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode -import org.jetbrains.kotlin.resolve.calls.inference.model.BuilderInferencePosition -import org.jetbrains.kotlin.types.model.* +import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage +import org.jetbrains.kotlin.types.model.KotlinTypeMarker +import org.jetbrains.kotlin.types.model.TypeConstructorMarker +import org.jetbrains.kotlin.types.model.freshTypeConstructor +import org.jetbrains.kotlin.types.model.safeSubstitute data class ReturnArgumentsAnalysisResult( val returnArguments: Collection, - val inferenceSession: FirInferenceSession? + val additionalConstraints: ConstraintStorage?, ) interface LambdaAnalyzer { @@ -35,8 +38,9 @@ interface LambdaAnalyzer { contextReceivers: List, parameters: List, expectedReturnType: ConeKotlinType?, // null means, that return type is not proper i.e. it depends on some type variables - stubsForPostponedVariables: Map, - candidate: Candidate + candidate: Candidate, + withPCLASession: Boolean, + forOverloadByLambdaReturnType: Boolean, ): ReturnArgumentsAnalysisResult } @@ -44,21 +48,24 @@ class PostponedArgumentsAnalyzer( private val resolutionContext: ResolutionContext, private val lambdaAnalyzer: LambdaAnalyzer, private val components: InferenceComponents, - private val callResolver: FirCallResolver + private val callResolver: FirCallResolver, ) { fun analyze( c: PostponedArgumentsAnalyzerContext, argument: PostponedResolvedAtom, candidate: Candidate, - completionMode: ConstraintSystemCompletionMode ) { when (argument) { is ResolvedLambdaAtom -> - analyzeLambda(c, argument, candidate, completionMode) + analyzeLambda(c, argument, candidate, forOverloadByLambdaReturnType = false) is LambdaWithTypeVariableAsExpectedTypeAtom -> - analyzeLambda(c, argument.transformToResolvedLambda(c.getBuilder(), resolutionContext), candidate, completionMode) + analyzeLambda( + c, + argument.transformToResolvedLambda(c.getBuilder(), resolutionContext), + candidate, forOverloadByLambdaReturnType = false + ) is ResolvedCallableReferenceAtom -> processCallableReference(argument, candidate) } @@ -66,7 +73,7 @@ class PostponedArgumentsAnalyzer( private fun processCallableReference(atom: ResolvedCallableReferenceAtom, candidate: Candidate) { if (atom.mightNeedAdditionalResolution) { - callResolver.resolveCallableReference(candidate.csBuilder, atom, hasSyntheticOuterCall = false) + callResolver.resolveCallableReference(candidate, atom, hasSyntheticOuterCall = false) } val callableReferenceAccess = atom.reference @@ -98,17 +105,26 @@ class PostponedArgumentsAnalyzer( c: PostponedArgumentsAnalyzerContext, lambda: ResolvedLambdaAtom, candidate: Candidate, - completionMode: ConstraintSystemCompletionMode, + forOverloadByLambdaReturnType: Boolean, //diagnosticHolder: KotlinDiagnosticsHolder ): ReturnArgumentsAnalysisResult { // TODO: replace with `require(!lambda.analyzed)` when KT-54767 will be fixed if (lambda.analyzed) { - return ReturnArgumentsAnalysisResult(lambda.returnStatements, inferenceSession = null) + return ReturnArgumentsAnalysisResult(lambda.returnStatements, additionalConstraints = null) } + val additionalBindings: Map? = + (resolutionContext.bodyResolveContext.inferenceSession as? FirPCLAInferenceSession)?.let { builderInferenceSession -> + // TODO: Fix variables for context receivers, too (KT-64859) + buildMap { + lambda.receiver + ?.let { builderInferenceSession.fixVariablesForMemberScope(it, candidate.system) } + ?.let(this::plusAssign) + } + } + val unitType = components.session.builtinTypes.unitType.type - val stubsForPostponedVariables = c.bindingStubsForPostponedVariables() - val currentSubstitutor = c.buildCurrentSubstitutor(stubsForPostponedVariables.mapKeys { it.key.freshTypeConstructor(c) }) + val currentSubstitutor = c.buildCurrentSubstitutor(additionalBindings ?: emptyMap()) as ConeSubstitutor fun substitute(type: ConeKotlinType) = currentSubstitutor.safeSubstitute(c, type) as ConeKotlinType @@ -126,21 +142,27 @@ class PostponedArgumentsAnalyzer( else -> null } + val withPCLASession = + lambda.inputTypes + .any { inputType -> + with(c) { inputType.extractTypeVariables() }.any(c.notFixedTypeVariables::contains) + } + val results = lambdaAnalyzer.analyzeAndGetLambdaReturnArguments( lambda, receiver, contextReceivers, parameters, expectedTypeForReturnArguments, - stubsForPostponedVariables, - candidate + candidate, + withPCLASession, + forOverloadByLambdaReturnType, ) applyResultsOfAnalyzedLambdaToCandidateSystem( c, lambda, candidate, results, - completionMode, ::substitute ) return results @@ -151,10 +173,13 @@ class PostponedArgumentsAnalyzer( lambda: ResolvedLambdaAtom, candidate: Candidate, results: ReturnArgumentsAnalysisResult, - completionMode: ConstraintSystemCompletionMode, - substitute: (ConeKotlinType) -> ConeKotlinType = c.createSubstituteFunctorForLambdaAnalysis() + substitute: (ConeKotlinType) -> ConeKotlinType = c.createSubstituteFunctorForLambdaAnalysis(), ) { - val (returnArguments, inferenceSession) = results + val (returnArguments, additionalConstraintStorage) = results + + if (additionalConstraintStorage != null) { + c.addOtherSystem(additionalConstraintStorage) + } val checkerSink: CheckerSink = CheckerSinkImpl(candidate) val builder = c.getBuilder() @@ -169,11 +194,6 @@ class PostponedArgumentsAnalyzer( // If the lambda returns Unit, the last expression is not returned and should not be constrained. val isLastExpression = it == lastExpression - // Don't add last call for builder-inference - // Note, that we don't use the same condition "(returnTypeRef.type.isUnitOrFlexibleUnit || lambda.atom.shouldReturnUnit(returnArguments))" - // as in the if below, because "lambda.atom.shouldReturnUnit(returnArguments)" might mean that the last statement is not completed - if (isLastExpression && lambdaExpectedTypeIsUnit && inferenceSession is FirBuilderInferenceSession) return@forEach - // TODO (KT-55837) questionable moment inherited from FE1.0 (the `haveSubsystem` case): // fun foo(): T // run { @@ -181,11 +201,25 @@ class PostponedArgumentsAnalyzer( // foo() // T = Unit, even though there is no implicit return // } // Things get even weirder if T has an upper bound incompatible with Unit. - // Not calling `addSubsystemFromExpression` for builder-inference is crucial val haveSubsystem = c.addSubsystemFromExpression(it) - if (isLastExpression && !haveSubsystem && - (lambdaExpectedTypeIsUnit || lambda.atom.shouldReturnUnit(returnArguments)) - ) return@forEach + val isUnitLambda = lambdaExpectedTypeIsUnit || lambda.atom.shouldReturnUnit(returnArguments) + if (isLastExpression && isUnitLambda) { + // That "if" is necessary because otherwise we would force a lambda return type + // to be inferred from completed last expression. + // See `test1` at testData/diagnostics/tests/inference/coercionToUnit/afterBareReturn.kt + if (haveSubsystem) { + // We don't force it because of the cases like + // buildMap { + // put("a", 1) // While `put` returns V, we should not enforce the latter to be a subtype of Unit + // } + // See KT-63602 for details. + builder.addSubtypeConstraintIfCompatible( + it.resolvedType, returnTypeRef.type, + ConeLambdaArgumentConstraintPosition(lambda.atom) + ) + } + return@forEach + } hasExpressionInReturnArguments = true if (!builder.hasContradiction) { @@ -212,25 +246,6 @@ class PostponedArgumentsAnalyzer( lambda.analyzed = true lambda.returnStatements = returnArguments c.resolveForkPointsConstraints() - - if (inferenceSession != null) { - val postponedVariables = inferenceSession.inferPostponedVariables(lambda, builder, completionMode, candidate) - - if (postponedVariables == null) { - builder.removePostponedVariables() - return - } - - for ((constructor, resultType) in postponedVariables) { - val variableWithConstraints = builder.currentStorage().notFixedTypeVariables[constructor] ?: continue - val variable = variableWithConstraints.typeVariable as ConeTypeVariable - - builder.unmarkPostponedVariable(variable) - builder.addSubtypeConstraint(resultType, variable.defaultType(c), BuilderInferencePosition) - } - - c.removePostponedTypeVariablesFromConstraints(postponedVariables.keys) - } } fun PostponedArgumentsAnalyzerContext.createSubstituteFunctorForLambdaAnalysis(): (ConeKotlinType) -> ConeKotlinType { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt index d116326ec9b..8b2c90976f8 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.fakeElement import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty import org.jetbrains.kotlin.fir.declarations.utils.isInline import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic @@ -22,6 +23,7 @@ import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer import org.jetbrains.kotlin.fir.resolve.diagnostics.* +import org.jetbrains.kotlin.fir.resolve.inference.FirTypeVariablesAfterPCLATransformer import org.jetbrains.kotlin.fir.resolve.inference.ResolvedLambdaAtom import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap @@ -32,7 +34,10 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirClassSubstitutionScope import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperator import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperatorForUnsignedType import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol -import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef import org.jetbrains.kotlin.fir.types.builder.buildStarProjection @@ -124,7 +129,7 @@ class FirCallCompletionResultsWriterTransformer( if (mode == Mode.DelegatedPropertyCompletion) { // Update type for `$delegateField` in `$$delegateField.get/setValue()` calls inside accessors - val typeUpdater = TypeUpdaterForDelegateArguments() + val typeUpdater = TypeUpdaterForPCLAAndDelegateReceivers() qualifiedAccessExpression.transformExplicitReceiver(typeUpdater, null) } @@ -137,6 +142,12 @@ class FirCallCompletionResultsWriterTransformer( extensionReceiver = extensionReceiver?.transformSingle(integerOperatorApproximator, expectedExtensionReceiverType) } + if (subCandidate.usedOuterCs) { + val updaterForThisReferences = TypeUpdaterForPCLAAndDelegateReceivers() + dispatchReceiver = dispatchReceiver?.transformSingle(updaterForThisReferences, null) + extensionReceiver = extensionReceiver?.transformSingle(updaterForThisReferences, null) + } + qualifiedAccessExpression.apply { replaceCalleeReference(calleeReference.toResolvedReference()) replaceDispatchReceiver(dispatchReceiver) @@ -171,6 +182,21 @@ class FirCallCompletionResultsWriterTransformer( if (declaration !is FirErrorFunction) { qualifiedAccessExpression.replaceTypeArguments(typeArguments) } + + for (postponedCall in subCandidate.postponedPCLACalls) { + postponedCall.transformSingle(this, null) + } + + for (callback in subCandidate.onCompletionResultsWritingCallbacks) { + callback(finalSubstitutor) + } + + // TODO: Be aware of exponent + val firStubTypeTransformer = FirTypeVariablesAfterPCLATransformer(finalSubstitutor) + for (lambda in subCandidate.lambdasAnalyzedWithPCLA) { + lambda.transformSingle(firStubTypeTransformer, null) + } + session.lookupTracker?.recordTypeResolveAsLookup(type, qualifiedAccessExpression.source, context.file.source) return qualifiedAccessExpression } @@ -195,7 +221,7 @@ class FirCallCompletionResultsWriterTransformer( * TODO: In future, it would be nice to get rid of it and there's actually a way to do it – not using substitution overrides (see KT-61618) */ private fun Candidate.updateSubstitutedMemberIfReceiverContainsTypeVariable() { - val updatedSymbol = symbol.updateSubstitutedMemberIfReceiverContainsTypeVariable() ?: return + val updatedSymbol = symbol.updateSubstitutedMemberIfReceiverContainsTypeVariable(usedOuterCs) ?: return val oldSymbol = symbol @OptIn(Candidate.UpdatingSymbol::class) @@ -221,9 +247,9 @@ class FirCallCompletionResultsWriterTransformer( } } - private fun FirBasedSymbol<*>.updateSubstitutedMemberIfReceiverContainsTypeVariable(): FirBasedSymbol<*>? { + private fun FirBasedSymbol<*>.updateSubstitutedMemberIfReceiverContainsTypeVariable(usedOuterCs: Boolean): FirBasedSymbol<*>? { // TODO: Add assertion that this function returns not-null only for BI and delegation inference - if (mode != Mode.DelegatedPropertyCompletion) return null + if (mode != Mode.DelegatedPropertyCompletion && !usedOuterCs) return null val fir = fir if (fir !is FirCallableDeclaration) return null @@ -240,6 +266,22 @@ class FirCallCompletionResultsWriterTransformer( ) as? FirClassSubstitutionScope ?: return null val original = fir.originalForSubstitutionOverride ?: return null + + if (fir is FirSyntheticProperty && fir.symbol is FirSimpleSyntheticPropertySymbol && original is FirSyntheticProperty) { + var result: FirBasedSymbol<*>? = null + FirSyntheticPropertiesScope.createIfSyntheticNamesProviderIsDefined(session, updatedDispatchReceiverType, scope) + ?.processPropertiesByName(fir.name) { + val newProperty = it.fir as? FirSyntheticProperty ?: return@processPropertiesByName + val originalForNew = newProperty.originalForSubstitutionOverride ?: return@processPropertiesByName + if (originalForNew.getter.delegate == original.getter.delegate) { + check(result == null) + result = it + } + } + + return result ?: error("Not found synthetic property: ${fir.renderWithType()}") + } + return findSingleSubstitutedSymbolWithOriginal(original.symbol) { processor -> when (original) { is FirSimpleFunction -> scope.processFunctionsByName(original.name, processor) @@ -519,10 +561,19 @@ class FirCallCompletionResultsWriterTransformer( } } + var dispatchReceiver = subCandidate.dispatchReceiverExpression() + var extensionReceiver = subCandidate.chosenExtensionReceiverExpression() + + if (subCandidate.usedOuterCs) { + val updaterForThisReferences = TypeUpdaterForPCLAAndDelegateReceivers() + dispatchReceiver = dispatchReceiver?.transformSingle(updaterForThisReferences, null) + extensionReceiver = extensionReceiver?.transformSingle(updaterForThisReferences, null) + } + return callableReferenceAccess.apply { replaceCalleeReference(resolvedReference) - replaceDispatchReceiver(subCandidate.dispatchReceiverExpression()) - replaceExtensionReceiver(subCandidate.chosenExtensionReceiverExpression()) + replaceDispatchReceiver(dispatchReceiver) + replaceExtensionReceiver(extensionReceiver) if (calleeReference.candidate.doesResolutionResultOverrideOtherToPreserveCompatibility()) { addNonFatalDiagnostic(ConeResolutionResultOverridesOtherToPreserveCompatibility) } @@ -533,25 +584,33 @@ class FirCallCompletionResultsWriterTransformer( return smartCastExpression.transformOriginalExpression(this, data) } - private inner class TypeUpdaterForDelegateArguments : FirTransformer() { + private inner class TypeUpdaterForPCLAAndDelegateReceivers : FirTransformer() { override fun transformElement(element: E, data: Any?): E { return element } + override fun transformThisReceiverExpression(thisReceiverExpression: FirThisReceiverExpression, data: Any?): FirStatement { + return transformTypeRefForQualifiedAccess(thisReceiverExpression) + } + override fun transformQualifiedAccessExpression( qualifiedAccessExpression: FirQualifiedAccessExpression, data: Any?, ): FirStatement { + return transformTypeRefForQualifiedAccess(qualifiedAccessExpression) + } + + override fun transformPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression, data: Any?): FirStatement { + return transformQualifiedAccessExpression(propertyAccessExpression, data) + } + + private fun transformTypeRefForQualifiedAccess(qualifiedAccessExpression: FirQualifiedAccessExpression): FirQualifiedAccessExpression { val originalType = qualifiedAccessExpression.resolvedType val substitutedReceiverType = finallySubstituteOrNull(originalType) ?: return qualifiedAccessExpression qualifiedAccessExpression.replaceConeTypeOrNull(substitutedReceiverType) session.lookupTracker?.recordTypeResolveAsLookup(substitutedReceiverType, qualifiedAccessExpression.source, context.file.source) return qualifiedAccessExpression } - - override fun transformPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression, data: Any?): FirStatement { - return transformQualifiedAccessExpression(propertyAccessExpression, data) - } } private fun FirTypeRef.substitute(candidate: Candidate): ConeKotlinType { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt index 0b465f4b43f..662f7d3f707 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt @@ -20,12 +20,9 @@ import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.calls.ImplicitExtensionReceiverValue import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue import org.jetbrains.kotlin.fir.resolve.calls.InaccessibleImplicitReceiverValue -import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext import org.jetbrains.kotlin.fir.resolve.dfa.DataFlowAnalyzerContext -import org.jetbrains.kotlin.fir.resolve.inference.FirBuilderInferenceSession -import org.jetbrains.kotlin.fir.resolve.inference.FirCallCompleter -import org.jetbrains.kotlin.fir.resolve.inference.FirDelegatedPropertyInferenceSession import org.jetbrains.kotlin.fir.resolve.inference.FirInferenceSession +import org.jetbrains.kotlin.fir.resolve.inference.FirPCLAInferenceSession import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator import org.jetbrains.kotlin.fir.resolve.transformers.withScopeCleanup import org.jetbrains.kotlin.fir.scopes.FirScope @@ -40,7 +37,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames.UNDERSCORE_FOR_UNUSED_VAR -import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.util.PrivateForInline class BodyResolveContext( @@ -57,6 +53,7 @@ class BodyResolveContext( @PrivateForInline var regularTowerDataContexts = FirRegularTowerDataContexts(regular = FirTowerDataContext()) + // TODO: Rename to postponed @PrivateForInline val specialTowerDataContexts = FirSpecialTowerDataContexts() @@ -108,16 +105,6 @@ class BodyResolveContext( } } - /** - * CS for an outer type system if it's relevant, for example, in the case of `val x by myGenericDelegateCall()`, - * relevant `getValue` call is expected to be resolved in the context of an outer CS built from `myGenericDelegateCall()` and - * probably using its type variables inside `getValue` candidates resolution. - * - * Note that it's not assumed to modify the storage, but only use it as a base system content for the candidate. - */ - @set:PrivateForInline - var outerConstraintStorage: ConstraintStorage = ConstraintStorage.Empty - val anonymousFunctionsAnalyzedInDependentContext: MutableSet> = mutableSetOf() var containingClassDeclarations: ArrayDeque = ArrayDeque() @@ -263,7 +250,6 @@ class BodyResolveContext( holder.scopeSession ) addReceiver(labelName, receiver, additionalLabelName) - (inferenceSession as? FirBuilderInferenceSession)?.addLambdaImplicitReceiver(receiver) } f() @@ -331,11 +317,11 @@ class BodyResolveContext( } @OptIn(PrivateForInline::class) - inline fun withInferenceSession(inferenceSession: FirInferenceSession, block: () -> R): R { + inline fun withInferenceSession(inferenceSession: S, block: S.() -> R): R { val oldSession = this.inferenceSession this.inferenceSession = inferenceSession return try { - block() + inferenceSession.block() } finally { this.inferenceSession = oldSession } @@ -352,10 +338,17 @@ class BodyResolveContext( } @PrivateForInline - inline fun withTemporaryRegularContext(newContext: FirTowerDataContext?, f: () -> T): T { - if (newContext == null) return f() + inline fun withTemporaryRegularContext(newContext: PostponedAtomsResolutionContext?, f: () -> T): T { + val (towerDataContext, newInferenceSession) = newContext ?: return f() + return withTowerDataModeCleanup { - withTowerDataContexts(regularTowerDataContexts.replaceAndSetActiveRegularContext(newContext), f) + withTowerDataContexts(regularTowerDataContexts.replaceAndSetActiveRegularContext(towerDataContext)) { + if (newInferenceSession !== this.inferenceSession) { + withInferenceSession(newInferenceSession) { f() } + } else { + f() + } + } } } @@ -382,7 +375,7 @@ class BodyResolveContext( // to use information from local class inside it. // However, we should not copy other kinds of inference sessions, // otherwise we can "inherit" type variables from there provoking inference problems - if (this@BodyResolveContext.inferenceSession is FirBuilderInferenceSession) { + if (this@BodyResolveContext.inferenceSession is FirPCLAInferenceSession) { inferenceSession = this@BodyResolveContext.inferenceSession } } @@ -756,7 +749,9 @@ class BodyResolveContext( @OptIn(PrivateForInline::class) fun storeContextForAnonymousFunction(anonymousFunction: FirAnonymousFunction) { - specialTowerDataContexts.storeAnonymousFunctionContext(anonymousFunction.symbol, towerDataContext) + specialTowerDataContexts.storeAnonymousFunctionContext( + anonymousFunction.symbol, towerDataContext, inferenceSession + ) } @OptIn(PrivateForInline::class) @@ -867,38 +862,6 @@ class BodyResolveContext( } } - inline fun forPropertyDelegateAccessors( - property: FirProperty, - resolutionContext: ResolutionContext, - callCompleter: FirCallCompleter, - f: FirDelegatedPropertyInferenceSession.() -> T - ) { - val inferenceSession = FirDelegatedPropertyInferenceSession( - property, - resolutionContext, - callCompleter.createPostponedArgumentsAnalyzer(resolutionContext) - ) - - withInferenceSession(inferenceSession) { - inferenceSession.f() - } - } - - @OptIn(PrivateForInline::class) - inline fun withOuterConstraintStorage( - storage: ConstraintStorage, - f: () -> T - ): T { - val oldStorage = this.outerConstraintStorage - this.outerConstraintStorage = storage - return try { - f() - } finally { - this.outerConstraintStorage = oldStorage - } - } - - @OptIn(PrivateForInline::class) inline fun withConstructor(constructor: FirConstructor, f: () -> T): T = withContainer(constructor, f) @@ -956,7 +919,8 @@ class BodyResolveContext( fun storeCallableReferenceContext(callableReferenceAccess: FirCallableReferenceAccess) { specialTowerDataContexts.storeCallableReferenceContext( callableReferenceAccess, - towerDataContext.createSnapshot(keepMutable = false) + towerDataContext.createSnapshot(keepMutable = false), + inferenceSession, ) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt index fab1578e985..5f263893578 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt @@ -38,7 +38,6 @@ import org.jetbrains.kotlin.fir.resolve.inference.ResolvedLambdaAtom import org.jetbrains.kotlin.fir.resolve.inference.extractLambdaInfoFromFunctionType import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor -import org.jetbrains.kotlin.fir.resolve.transformers.FirCallCompletionResultsWriterTransformer import org.jetbrains.kotlin.fir.resolve.transformers.FirStatusResolver import org.jetbrains.kotlin.fir.resolve.transformers.contracts.runContractResolveForFunction import org.jetbrains.kotlin.fir.resolve.transformers.transformVarargTypeToArrayType @@ -329,89 +328,82 @@ open class FirDeclarationsResolveTransformer( private fun transformPropertyAccessorsWithDelegate( property: FirProperty, - delegate: FirExpression, + delegateContainer: FirExpression, shouldResolveEverything: Boolean, ) { - val isImplicitTypedProperty = property.returnTypeRef is FirImplicitTypeRef + require(delegateContainer is FirWrappedDelegateExpression) + dataFlowAnalyzer.enterDelegateExpression() - context.forPropertyDelegateAccessors(property, resolutionContext, callCompleter) { - dataFlowAnalyzer.enterDelegateExpression() - // Resolve delegate expression, after that, delegate will contain either expr.provideDelegate or expr + // First, resolve delegate expression in dependent context withing existing (possibly Default) inference session + val delegateExpression = + // Resolve delegate expression; after that, delegate will contain either expr.provideDelegate or expr if (property.isLocal) { - property.transformDelegate(transformer, ResolutionMode.ContextDependent.Delegate) + transformDelegateExpression(delegateContainer) } else { context.forPropertyInitializer { - property.transformDelegate(transformer, ResolutionMode.ContextDependent.Delegate) + transformDelegateExpression(delegateContainer) } } - // We don't use inference from setValue calls (i.e. don't resolve setters until the delegate inference is completed), - // when property doesn't have explicit type. + // Then, create a root/child inference session based on the resolved delegate expression + context.withInferenceSession( + FirDelegatedPropertyInferenceSession( + resolutionContext, + callCompleter, + delegateExpression, + ) + ) { + property.replaceDelegate( + getResolvedProvideDelegateIfSuccessful(delegateContainer.provideDelegateCall, delegateExpression) + ?: delegateExpression + ) + + // We don't use inference from setValue calls (i.e., don't resolve setters until the delegate inference is completed) + // when the property doesn't have an explicit type. // It's necessary because we need to supply the property type as the 3rd argument for `setValue` and there might be uninferred // variables from `getValue`. // The same logic was used at K1 (see org.jetbrains.kotlin.resolve.DelegatedPropertyResolver.inferDelegateTypeFromGetSetValueMethods) + val isImplicitTypedProperty = property.returnTypeRef is FirImplicitTypeRef property.transformAccessors( if (isImplicitTypedProperty) SetterResolutionMode.SKIP else SetterResolutionMode.FULLY_RESOLVE, shouldResolveEverything, ) - val completedCalls = completeCandidates() - - val finalSubstitutor = createFinalSubstitutor() - - finalSubstitutor.substituteOrNull(property.returnTypeRef.coneType)?.let { substitutedType -> - property.replaceReturnTypeRef(property.returnTypeRef.withReplacedConeType(substitutedType)) - } - property.getter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true) - property.setter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true) - property.replaceReturnTypeRef( - property.returnTypeRef.approximateDeclarationType( - session, - property.visibilityForApproximation(), - property.isLocal + completeSessionOrPostponeIfNonRoot { finalSubstitutor -> + finalSubstitutor.substituteOrNull(property.returnTypeRef.coneType)?.let { substitutedType -> + property.replaceReturnTypeRef(property.returnTypeRef.withReplacedConeType(substitutedType)) + } + property.getter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true) + property.setter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true) + property.replaceReturnTypeRef( + property.returnTypeRef.approximateDeclarationType( + session, + property.visibilityForApproximation(), + property.isLocal + ) ) - ) - val callCompletionResultsWriter = callCompleter.createCompletionResultsWriter( - finalSubstitutor, - mode = FirCallCompletionResultsWriterTransformer.Mode.DelegatedPropertyCompletion - ) - completedCalls.forEach { - it.transformSingle(callCompletionResultsWriter, null) + // `isImplicitTypedProperty` means we haven't run setter resolution yet (see its second usage) + if (isImplicitTypedProperty) { + property.resolveSetter(mayResolveSetterBody = true, shouldResolveEverything = shouldResolveEverything) + } + + dataFlowAnalyzer.exitDelegateExpression(delegateContainer) } } - - // `isImplicitTypedProperty` means we haven't run setter resolution yet (see its second usage) - if (isImplicitTypedProperty) { - property.resolveSetter(mayResolveSetterBody = true, shouldResolveEverything = shouldResolveEverything) - } - - dataFlowAnalyzer.exitDelegateExpression(delegate) } - override fun transformPropertyAccessor(propertyAccessor: FirPropertyAccessor, data: ResolutionMode): FirPropertyAccessor { - return propertyAccessor.also { - transformProperty(it.propertySymbol.fir, data) - } - } - - override fun transformWrappedDelegateExpression( - wrappedDelegateExpression: FirWrappedDelegateExpression, - data: ResolutionMode, - ): FirStatement { - // First, resolve delegate expression in dependent context, and add potentially partially resolved call to inference session - // (that is why we use ResolutionMode.ContextDependent.Delegate instead of plain ContextDependent) - val delegateExpression = wrappedDelegateExpression.expression.transformSingle(transformer, ResolutionMode.ContextDependent.Delegate) - .transformSingle(components.integerLiteralAndOperatorApproximationTransformer, null) - - val provideDelegateCall = wrappedDelegateExpression.provideDelegateCall - provideDelegateCall.replaceExplicitReceiver(delegateExpression) + private fun getResolvedProvideDelegateIfSuccessful( + provideDelegateCall: FirFunctionCall, + resolvedDelegateExpression: FirExpression, + ): FirFunctionCall? { + provideDelegateCall.replaceExplicitReceiver(resolvedDelegateExpression) // Resolve call for provideDelegate, without completion // TODO: this generates some nodes in the control flow graph which we don't want if we // end up not selecting this option, KT-59684 transformer.expressionsTransformer?.transformFunctionCallInternal( - provideDelegateCall, ResolutionMode.ContextDependent, provideDelegate = true + provideDelegateCall, ResolutionMode.ReceiverResolution, provideDelegate = true ) // If we got successful candidate for provideDelegate, let's select it @@ -438,10 +430,19 @@ open class FirDeclarationsResolveTransformer( return provideDelegateCall } - // Select delegate expression otherwise - return delegateExpression + return null } + override fun transformPropertyAccessor(propertyAccessor: FirPropertyAccessor, data: ResolutionMode): FirPropertyAccessor { + return propertyAccessor.also { + transformProperty(it.propertySymbol.fir, data) + } + } + + private fun transformDelegateExpression(delegate: FirWrappedDelegateExpression): FirExpression = + delegate.expression.transformSingle(transformer, ResolutionMode.ContextDependent.Delegate) + .transformSingle(components.integerLiteralAndOperatorApproximationTransformer, null) + /** * For supporting the case when `provideDelegate` has a signature with type variable as a return type, like * fun K.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = this @@ -481,11 +482,6 @@ open class FirDeclarationsResolveTransformer( val candidateSystem = candidate.system val candidateStorage = candidateSystem.currentStorage() - val allTypeVariables = candidateStorage.allTypeVariables.keys.toList() - // Subset of type variables obtained from the `provideDelegate` call - val typeVariablesRelatedToProvideDelegate = - allTypeVariables.subList(candidateStorage.outerSystemVariablesPrefixSize, allTypeVariables.size).toSet() - val variableWithConstraints = candidateSystem.notFixedTypeVariables[typeVariable] ?: error("Not found type variable $typeVariable") @@ -494,7 +490,7 @@ open class FirDeclarationsResolveTransformer( // Temporary declare all the "outer" variables as proper (i.e., all inner variables as improper) // Without that, all variables (both inner and outer ones) would be considered as improper, // while we want to fix to assume `Delegate` as proper because `Tv` belongs to the outer system - candidateSystem.withTypeVariablesThatAreNotCountedAsProperTypes(typeVariablesRelatedToProvideDelegate) { + candidateSystem.withTypeVariablesThatAreCountedAsProperTypes(candidateSystem.outerTypeVariables.orEmpty()) { // TODO: reconsider the approach here (KT-61781 for tracking) // Actually, this code might fail with an exception in some rare cases (see KT-61781) // The problem is that in the issue example, when fixing T type variable, it has two upper bounds: X and Delegate @@ -506,7 +502,7 @@ open class FirDeclarationsResolveTransformer( // it seems like they do something relevant. resultType = inferenceComponents.resultTypeResolver.findResultTypeOrNull( candidateSystem, variableWithConstraints, TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN - ) as? ConeKotlinType ?: return@withTypeVariablesThatAreNotCountedAsProperTypes + ) as? ConeKotlinType ?: return@withTypeVariablesThatAreCountedAsProperTypes check(!candidateStorage.hasContradiction) { "We only should try fixing variables on successful provideDelegate candidate" } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index 34533b81431..4c49bda9471 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -31,7 +31,6 @@ import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.diagnostics.* -import org.jetbrains.kotlin.fir.resolve.inference.FirStubInferenceSession import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.transformers.replaceLambdaArgumentInvocationKinds import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperator @@ -91,7 +90,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT transformQualifiedAccessExpression(qualifiedAccessExpression, data, isUsedAsReceiver = false, isUsedAsGetClassReceiver = false) } - fun transformQualifiedAccessExpression( + private fun transformQualifiedAccessExpression( qualifiedAccessExpression: FirQualifiedAccessExpression, data: ResolutionMode, isUsedAsReceiver: Boolean, @@ -438,9 +437,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT functionCall } - val resultExpression = context.inferenceSession.onCandidatesResolution(withTransformedArguments) { - callResolver.resolveCallAndSelectCandidate(withTransformedArguments, data) - } + val resultExpression = callResolver.resolveCallAndSelectCandidate(withTransformedArguments, data) val completeInference = callCompleter.completeCall(resultExpression, data) val result = completeInference.transformToIntegerOperatorCallOrApproximateItIfNeeded(data) @@ -1035,6 +1032,13 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT ), ) + // for cases like + // buildSomething { tVar = "" // Should infer TV from String assignment } + context.inferenceSession.addSubtypeConstraintIfCompatible( + variableAssignment.lValue.resolvedType, variableAssignment.rValue.resolvedType, + variableAssignment, + ) + dataFlowAnalyzer.exitVariableAssignment(result) return result @@ -1053,10 +1057,10 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT val transformedLHS = when (explicitReceiver) { is FirPropertyAccessExpression -> transformQualifiedAccessExpression( - explicitReceiver, ResolutionMode.ContextIndependent, isUsedAsReceiver = true, isUsedAsGetClassReceiver = false + explicitReceiver, ResolutionMode.ReceiverResolution.ForCallableReference, isUsedAsReceiver = true, isUsedAsGetClassReceiver = false ) as FirExpression else -> - explicitReceiver?.transformSingle(this, ResolutionMode.ContextIndependent) + explicitReceiver?.transformSingle(this, ResolutionMode.ReceiverResolution.ForCallableReference) }.apply { if (this is FirResolvedQualifier && callableReferenceAccess.hasQuestionMarkAtLHS) { replaceIsNullableLHSForCallableReference(true) @@ -1181,7 +1185,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT constExpression.replaceKind(expressionType.toConstKind() as ConstantValueKind) expressionType } - data is ResolutionMode.ReceiverResolution -> { + data is ResolutionMode.ReceiverResolution && !data.forCallableReference -> { require(expressionType is ConeIntegerLiteralConstantTypeImpl) ConeIntegerConstantOperatorTypeImpl(expressionType.isUnsigned, ConeNullability.NOT_NULL) } @@ -1740,4 +1744,4 @@ private fun FirFunctionCall.setArrayAugmentedAssignSource(operation: FirOperatio this.source = newSource this.resolvedSymbol = oldCalleeReference.resolvedSymbol }) -} \ No newline at end of file +} diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt index e1854f38bc4..7e4e9d36dd4 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.resolve.calls import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.fir.declarations.FirFunction +import org.jetbrains.kotlin.fir.declarations.FirTypeParameter import org.jetbrains.kotlin.fir.declarations.FirValueParameter import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression @@ -15,7 +16,6 @@ import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeTypeVariable import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget @@ -158,6 +158,7 @@ object AdaptedCallableReferenceIsUsedWithReflection : ResolutionDiagnostic(RESOL object TypeParameterAsExpression : ResolutionDiagnostic(INAPPLICABLE) -class StubBuilderInferenceReceiver( - val typeParameterSymbol: FirTypeParameterSymbol -) : ResolutionDiagnostic(RESOLVED_WITH_ERROR) +class TypeVariableAsExplicitReceiver( + val explicitReceiver: FirExpression, + val typeParameter: FirTypeParameter, +) : ResolutionDiagnostic(RESOLVED_WITH_ERROR) \ No newline at end of file diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzerContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzerContext.kt index 68cd259a8d8..d54f073d063 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzerContext.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzerContext.kt @@ -7,10 +7,13 @@ package org.jetbrains.kotlin.resolve.calls.components import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage +import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints import org.jetbrains.kotlin.types.model.* interface PostponedArgumentsAnalyzerContext : TypeSystemInferenceExtensionContext { - fun buildCurrentSubstitutor(additionalBindings: Map): TypeSubstitutorMarker + val notFixedTypeVariables: Map + + fun buildCurrentSubstitutor(additionalBindings: Map): TypeSubstitutorMarker fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker fun bindingStubsForPostponedVariables(): Map diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt index 82761384d72..9adfaff6926 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt @@ -11,21 +11,16 @@ import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker +import org.jetbrains.kotlin.types.model.K2Only import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.TypeConstructorMarker import org.jetbrains.kotlin.types.model.TypeVariableMarker abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Context, ResultTypeResolver.Context { - abstract val allTypeVariables: Map abstract override val notFixedTypeVariables: Map abstract override val fixedTypeVariables: Map abstract override val postponedTypeVariables: List - /** - * See [org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.outerSystemVariablesPrefixSize] - */ - abstract val outerSystemVariablesPrefixSize: Int - abstract fun getBuilder(): ConstraintSystemBuilder // type can be proper if it not contains not fixed type variables @@ -69,7 +64,7 @@ abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Contex completionMode: ConstraintSystemCompletionMode, analyze: (A) -> Unit ): Boolean { - if (completionMode == ConstraintSystemCompletionMode.FULL) { + if (completionMode.allLambdasShouldBeAnalyzed) { val argumentWithTypeVariableAsExpectedType = findPostponedArgumentWithRevisableExpectedType(postponedArguments) if (argumentWithTypeVariableAsExpectedType != null) { @@ -120,4 +115,11 @@ abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Contex !it.typeConstructor().isClassTypeConstructor() && !it.typeConstructor().isTypeParameterTypeConstructor() } }.map { it.type } + + /** + * @see [org.jetbrains.kotlin.resolve.calls.inference.components.VariableFixationFinder.Context.typeVariablesThatAreNotCountedAsProperTypes] + * @see [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded] + */ + @K2Only + abstract fun withTypeVariablesThatAreCountedAsProperTypes(typeVariables: Set, block: () -> R): R } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionMode.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionMode.kt index 679c171f8ac..62dea37cba5 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionMode.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionMode.kt @@ -5,8 +5,9 @@ package org.jetbrains.kotlin.resolve.calls.inference.components -enum class ConstraintSystemCompletionMode { - FULL, +enum class ConstraintSystemCompletionMode(val allLambdasShouldBeAnalyzed: Boolean) { + FULL(true), + PCLA_POSTPONED_CALL(true), /** * This mode allows us to infer variables in calls, which have enough type-info to be completed right-away @@ -18,6 +19,6 @@ enum class ConstraintSystemCompletionMode { * x.plus(run { x }) // Here, to select plus overload we need to analyze lambda * ``` */ - PARTIAL, - UNTIL_FIRST_LAMBDA + PARTIAL(false), + UNTIL_FIRST_LAMBDA(false), } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt index 79d1edb3c94..088a271a679 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt @@ -9,8 +9,13 @@ import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind import org.jetbrains.kotlin.builtins.functions.isBasicFunctionOrKFunction import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings -import org.jetbrains.kotlin.resolve.calls.inference.model.* -import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind +import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints +import org.jetbrains.kotlin.resolve.calls.model.LambdaWithTypeVariableAsExpectedTypeMarker +import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType +import org.jetbrains.kotlin.resolve.calls.model.PostponedCallableReferenceMarker +import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker import org.jetbrains.kotlin.types.model.* import org.jetbrains.kotlin.utils.SmartSet import java.util.* @@ -531,24 +536,40 @@ class PostponedArgumentInputTypesResolver( postponedArguments: List, topLevelType: KotlinTypeMarker, dependencyProvider: TypeVariableDependencyInformationProvider, - resolvedAtomProvider: ResolvedAtomProvider - ): Boolean = with(c) { - val expectedType = argument.run { (this as? PostponedAtomWithRevisableExpectedType)?.revisedExpectedType ?: expectedType } + resolvedAtomProvider: ResolvedAtomProvider, + ): Boolean { + val expectedType = argument.expectedFunctionType(c) ?: return false - if (expectedType != null && expectedType.isFunctionOrKFunctionWithAnySuspendability()) { - val wasFixedSomeVariable = c.fixNextReadyVariableForParameterType( - expectedType, - postponedArguments, - topLevelType, - dependencyProvider, - resolvedAtomProvider - ) + return c.fixNextReadyVariableForParameterType( + expectedType, + postponedArguments, + topLevelType, + dependencyProvider, + resolvedAtomProvider, + ) + } - if (wasFixedSomeVariable) - return true - } + @K2Only + fun findNextVariableForReportingNotInferredInputType( + c: Context, + argument: PostponedResolvedAtomMarker, + postponedArguments: List, + topLevelType: KotlinTypeMarker, + dependencyProvider: TypeVariableDependencyInformationProvider, + ): VariableFixationFinder.VariableForFixation? { + val expectedType = argument.expectedFunctionType(c) ?: return null - return false + return c.findNextVariableForParameterType( + expectedType, + dependencyProvider, + postponedArguments, + topLevelType, + ) + } + + private fun PostponedResolvedAtomMarker.expectedFunctionType(c: Context): KotlinTypeMarker? = with(c) { + val expectedType = (this@expectedFunctionType as? PostponedAtomWithRevisableExpectedType)?.revisedExpectedType ?: expectedType + expectedType?.takeIf { it.isFunctionOrKFunctionWithAnySuspendability() } } private fun Context.fixNextReadyVariableForParameterType( @@ -558,17 +579,9 @@ class PostponedArgumentInputTypesResolver( dependencyProvider: TypeVariableDependencyInformationProvider, resolvedAtomByTypeVariableProvider: ResolvedAtomProvider, ): Boolean = with(resolutionTypeSystemContext) { - val relatedVariables = type.extractArgumentsForFunctionTypeOrSubtype() - .flatMap { getAllDeeplyRelatedTypeVariables(it, dependencyProvider) } - val variableForFixation = variableFixationFinder.findFirstVariableForFixation( - this@fixNextReadyVariableForParameterType, - relatedVariables, - postponedArguments, - ConstraintSystemCompletionMode.FULL, - topLevelType - ) + val variableForFixation = findNextVariableForParameterType(type, dependencyProvider, postponedArguments, topLevelType) - if (variableForFixation == null || !variableForFixation.hasProperConstraint) + if (variableForFixation == null || !variableForFixation.isReady) return false val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) @@ -589,6 +602,26 @@ class PostponedArgumentInputTypesResolver( return true } + private fun Context.findNextVariableForParameterType( + type: KotlinTypeMarker, + dependencyProvider: TypeVariableDependencyInformationProvider, + postponedArguments: List, + topLevelType: KotlinTypeMarker, + ): VariableFixationFinder.VariableForFixation? { + val outerTypeVariables = outerTypeVariables.orEmpty() + val relatedVariables = type.extractArgumentsForFunctionTypeOrSubtype() + .flatMap { getAllDeeplyRelatedTypeVariables(it, dependencyProvider) } + .filter { it !in outerTypeVariables } + + return variableFixationFinder.findFirstVariableForFixation( + this, + relatedVariables, + postponedArguments, + ConstraintSystemCompletionMode.FULL, + topLevelType, + ) + } + private fun KotlinTypeMarker?.wrapToTypeWithKind() = this?.let { TypeWithKind(it) } companion object { diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt index de12ac5c1c4..ebef7dc5969 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt @@ -22,6 +22,8 @@ class ResultTypeResolver( private val languageVersionSettings: LanguageVersionSettings ) { interface Context : TypeSystemInferenceExtensionContext { + val notFixedTypeVariables: Map + val outerSystemVariablesPrefixSize: Int fun isProperType(type: KotlinTypeMarker): Boolean fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker fun isReified(variable: TypeVariableMarker): Boolean @@ -220,6 +222,10 @@ class ResultTypeResolver( if (typesWithoutStubs.isNotEmpty()) { commonSuperType = computeCommonSuperType(typesWithoutStubs) + } else if (outerSystemVariablesPrefixSize > 0) { + // outerSystemVariablesPrefixSize > 0 only for PCLA (K2) + @OptIn(K2Only::class) + commonSuperType = createSubstitutionFromSubtypingStubTypesToTypeVariables().safeSubstitute(commonSuperType) } } @@ -273,6 +279,14 @@ class ResultTypeResolver( } if (!atLeastOneProper) return emptyList() + + // PCLA slow path + // We only allow using TVs fixation for nested PCLA calls + if (outerSystemVariablesPrefixSize > 0) { + val notFixedToStubTypesSubstitutor = buildNotFixedVariablesToStubTypesSubstitutor() + return lowerConstraintTypes.map { notFixedToStubTypesSubstitutor.safeSubstitute(it) } + } + if (!atLeastOneNonProper) return lowerConstraintTypes val notFixedToStubTypesSubstitutor = buildNotFixedVariablesToStubTypesSubstitutor() @@ -336,7 +350,7 @@ class ResultTypeResolver( } private fun Context.isProperTypeForFixation(type: KotlinTypeMarker): Boolean = - isProperTypeForFixation(type) { isProperType(it) } + isProperTypeForFixation(type, notFixedTypeVariables.keys) { isProperType(it) } private fun findResultIfThereIsEqualsConstraint(c: Context, variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? = with(c) { diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt index 7b7ae720a79..0d502ebe239 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt @@ -7,15 +7,22 @@ package org.jetbrains.kotlin.resolve.calls.inference.components import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker -import org.jetbrains.kotlin.types.model.* +import org.jetbrains.kotlin.types.model.KotlinTypeMarker +import org.jetbrains.kotlin.types.model.TypeConstructorMarker +import org.jetbrains.kotlin.types.model.freshTypeConstructor +import org.jetbrains.kotlin.types.model.typeConstructor import org.jetbrains.kotlin.utils.SmartSet class TypeVariableDependencyInformationProvider( private val notFixedTypeVariables: Map, private val postponedKtPrimitives: List, private val topLevelType: KotlinTypeMarker?, - private val typeSystemContext: TypeSystemInferenceExtensionContext + private val typeSystemContext: VariableFixationFinder.Context ) { + + private val outerTypeVariables: Set? = + typeSystemContext.outerTypeVariables + /* * Not oriented edges * TypeVariable(A) has UPPER(Function1) => A and B are related deeply @@ -41,7 +48,16 @@ class TypeVariableDependencyInformationProvider( computeRelatedToTopLevelType() } - fun isVariableRelatedToTopLevelType(variable: TypeConstructorMarker) = relatedToTopLevelType.contains(variable) + fun isVariableRelatedToTopLevelType(variable: TypeConstructorMarker) = + relatedToTopLevelType.contains(variable) + + + fun isRelatedToOuterTypeVariable(variable: TypeConstructorMarker): Boolean { + val outerTypeVariables = outerTypeVariables ?: return false + val myDependent = getDeeplyDependentVariables(variable) ?: return false + return myDependent.any { it in outerTypeVariables } + } + fun isVariableRelatedToAnyOutputType(variable: TypeConstructorMarker) = relatedToAllOutputTypes.contains(variable) fun getDeeplyDependentVariables(variable: TypeConstructorMarker) = deepTypeVariableDependencies[variable] diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt index 8eb18a95fd8..24cb73c091f 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt @@ -27,25 +27,40 @@ class VariableFixationFinder( val fixedTypeVariables: Map val postponedTypeVariables: List val constraintsFromAllForkPoints: MutableList> + val allTypeVariables: Map /** - * If not null, that property means that we should assume temporary - * `allTypeVariables.keys.minus(typeVariablesThatAreNotCountedAsProperTypes)` as proper types when fixating some variables. + * See [org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.outerSystemVariablesPrefixSize] + */ + val outerSystemVariablesPrefixSize: Int + + val outerTypeVariables: Set? + get() = + when { + outerSystemVariablesPrefixSize > 0 -> allTypeVariables.keys.take(outerSystemVariablesPrefixSize).toSet() + else -> null + } + + /** + * If not null, that property means that we should assume temporary them all as proper types when fixating some variables. * * By default, if that property is null, we assume all `allTypeVariables` as not proper. * * Currently, that is only used for `provideDelegate` resolution, see * [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded] */ - val typeVariablesThatAreNotCountedAsProperTypes: Set? + val typeVariablesThatAreCountedAsProperTypes: Set? fun isReified(variable: TypeVariableMarker): Boolean } - data class VariableForFixation( + class VariableForFixation( val variable: TypeConstructorMarker, - val hasProperConstraint: Boolean, - ) + private val hasProperConstraint: Boolean, + private val hasDependencyOnOuterTypeVariable: Boolean = false, + ) { + val isReady: Boolean get() = hasProperConstraint && !hasDependencyOnOuterTypeVariable + } fun findFirstVariableForFixation( c: Context, @@ -53,11 +68,13 @@ class VariableFixationFinder( postponedKtPrimitives: List, completionMode: ConstraintSystemCompletionMode, topLevelType: KotlinTypeMarker, - ): VariableForFixation? = c.findTypeVariableForFixation(allTypeVariables, postponedKtPrimitives, completionMode, topLevelType) + ): VariableForFixation? = + c.findTypeVariableForFixation(allTypeVariables, postponedKtPrimitives, completionMode, topLevelType) enum class TypeVariableFixationReadiness { FORBIDDEN, WITHOUT_PROPER_ARGUMENT_CONSTRAINT, // proper constraint from arguments -- not from upper bound for type parameters + OUTER_TYPE_VARIABLE_DEPENDENCY, READY_FOR_FIXATION_DECLARED_UPPER_BOUND_WITH_SELF_TYPES, WITH_COMPLEX_DEPENDENCY, // if type variable T has constraint with non fixed type variable inside (non-top-level): T <: Foo ALL_CONSTRAINTS_TRIVIAL_OR_NON_PROPER, // proper trivial constraint from arguments, Nothing <: T @@ -85,6 +102,7 @@ class VariableFixationFinder( isTypeInferenceForSelfTypesSupported && areAllProperConstraintsSelfTypeBased(variable) -> TypeVariableFixationReadiness.READY_FOR_FIXATION_DECLARED_UPPER_BOUND_WITH_SELF_TYPES !variableHasProperArgumentConstraints(variable) -> TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT + dependencyProvider.isRelatedToOuterTypeVariable(variable) -> TypeVariableFixationReadiness.OUTER_TYPE_VARIABLE_DEPENDENCY hasDependencyToOtherTypeVariables(variable) -> TypeVariableFixationReadiness.WITH_COMPLEX_DEPENDENCY // TODO: Consider removing this kind of readiness, see KT-63032 allConstraintsTrivialOrNonProper(variable) -> TypeVariableFixationReadiness.ALL_CONSTRAINTS_TRIVIAL_OR_NON_PROPER @@ -152,7 +170,7 @@ class VariableFixationFinder( if (allTypeVariables.isEmpty()) return null val dependencyProvider = TypeVariableDependencyInformationProvider( - notFixedTypeVariables, postponedArguments, topLevelType.takeIf { completionMode == PARTIAL }, this + notFixedTypeVariables, postponedArguments, topLevelType.takeIf { completionMode == PARTIAL }, this, ) val candidate = @@ -161,6 +179,8 @@ class VariableFixationFinder( return when (getTypeVariableReadiness(candidate, dependencyProvider)) { TypeVariableFixationReadiness.FORBIDDEN -> null TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT -> VariableForFixation(candidate, false) + TypeVariableFixationReadiness.OUTER_TYPE_VARIABLE_DEPENDENCY -> + VariableForFixation(candidate, hasProperConstraint = true, hasDependencyOnOuterTypeVariable = true) else -> VariableForFixation(candidate, true) } @@ -190,12 +210,13 @@ class VariableFixationFinder( && !c.isNullabilityConstraint private fun Context.isProperType(type: KotlinTypeMarker): Boolean = - isProperTypeForFixation(type) { t -> !t.contains { isNotFixedRelevantVariable(it) } } + isProperTypeForFixation(type, notFixedTypeVariables.keys) { t -> !t.contains { isNotFixedRelevantVariable(it) } } private fun Context.isNotFixedRelevantVariable(it: KotlinTypeMarker): Boolean { - if (!notFixedTypeVariables.containsKey(it.typeConstructor())) return false - if (typeVariablesThatAreNotCountedAsProperTypes == null) return true - return typeVariablesThatAreNotCountedAsProperTypes!!.contains(it.typeConstructor()) + val key = it.typeConstructor() + if (!notFixedTypeVariables.containsKey(key)) return false + if (typeVariablesThatAreCountedAsProperTypes?.contains(key) == true) return false + return true } private fun Context.isReified(variable: TypeConstructorMarker): Boolean = @@ -235,8 +256,25 @@ class VariableFixationFinder( } } -inline fun TypeSystemInferenceExtensionContext.isProperTypeForFixation(type: KotlinTypeMarker, isProper: (KotlinTypeMarker) -> Boolean) = - isProper(type) && extractProjectionsForAllCapturedTypes(type).all(isProper) +/** + * Returns `false` for fixed type variables types even if `isProper(type) == true` + * Thus allowing only non-TVs types to be used for fixation on top level. + * While this limitation is important, it doesn't really limit final results because when we have a constraint like T <: E or E <: T + * and we're going to fix T into E, we assume that if E has some other constraints, they are being incorporated to T, so we would choose + * them instead of E itself. + */ +inline fun TypeSystemInferenceExtensionContext.isProperTypeForFixation( + type: KotlinTypeMarker, + notFixedTypeVariables: Set, + isProper: (KotlinTypeMarker) -> Boolean +): Boolean { + // We don't allow fixing T into any top-level TV type, like T := F or T := F & Any + // Even if F is considered as a proper by `isProper` (e.g., it belongs to an outer CS) + // But at the same time, we don't forbid fixing into T := MutableList + if (type.typeConstructor() in notFixedTypeVariables) return false + + return isProper(type) && extractProjectionsForAllCapturedTypes(type).all(isProper) +} fun TypeSystemInferenceExtensionContext.extractProjectionsForAllCapturedTypes(baseType: KotlinTypeMarker): Set { if (baseType.isFlexible()) { diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt index 9f8fb0ff878..40d87ec8d44 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt @@ -49,6 +49,8 @@ interface ConstraintStorage { val constraintsFromAllForkPoints: List> /** + * Outer system for a call means some set of variables defined beside it/its arguments + * * In case some candidate's CS is built in the context of some outer CS, first [outerSystemVariablesPrefixSize] in the list * of [allTypeVariables] belong to the outer CS. * @@ -62,6 +64,8 @@ interface ConstraintStorage { */ val outerSystemVariablesPrefixSize: Int + val usesOuterCs: Boolean + object Empty : ConstraintStorage { override val allTypeVariables: Map get() = emptyMap() override val notFixedTypeVariables: Map get() = emptyMap() @@ -77,6 +81,8 @@ interface ConstraintStorage { override val constraintsFromAllForkPoints: List> = emptyList() override val outerSystemVariablesPrefixSize: Int get() = 0 + + override val usesOuterCs: Boolean get() = false } } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt index 700ad4e1b21..2b85e3dd829 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt @@ -221,6 +221,14 @@ class MutableVariableWithConstraints private constructor( } } + fun runConstraintsSimplification() { + val currentState = constraints.toList() + mutableConstraints.apply { + clear() + addAll(currentState) + } + } + private fun isUsefulConstraint(constraint: Constraint, equalityConstraints: Map>): Boolean { if (constraint.kind == ConstraintKind.EQUALITY) return true return equalityConstraints[constraint.typeHashCode]?.none { it.type == constraint.type } ?: true @@ -251,4 +259,15 @@ internal class MutableConstraintStorage : ConstraintStorage { override val constraintsFromAllForkPoints: MutableList> = SmartList() override var outerSystemVariablesPrefixSize: Int = 0 + + override var usesOuterCs: Boolean = false + + @AssertionsOnly + internal var outerCS: ConstraintStorage? = null } + +/** + * Annotated member is used only for assertion purposes and does not affect semantics + */ +@RequiresOptIn +annotation class AssertionsOnly diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt index d14628c9186..a8b8e83f8f8 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt @@ -41,7 +41,7 @@ class NewConstraintSystemImpl( private val properTypesCache: MutableSet = SmartSet.create() private val notProperTypesCache: MutableSet = SmartSet.create() private val intersectionTypesCache: MutableMap, EmptyIntersectionTypeInfo?> = mutableMapOf() - override var typeVariablesThatAreNotCountedAsProperTypes: Set? = null + override var typeVariablesThatAreCountedAsProperTypes: Set? = null private var couldBeResolvedWithUnrestrictedBuilderInference: Boolean = false @@ -51,23 +51,26 @@ class NewConstraintSystemImpl( * @see [org.jetbrains.kotlin.resolve.calls.inference.components.VariableFixationFinder.Context.typeVariablesThatAreNotCountedAsProperTypes] * @see [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded] */ - fun withTypeVariablesThatAreNotCountedAsProperTypes(typeVariables: Set, block: () -> Unit) { + @K2Only + override fun withTypeVariablesThatAreCountedAsProperTypes(typeVariables: Set, block: () -> R): R { checkState(State.BUILDING) // Cleaning cache is necessary because temporarily we change the meaning of what does "proper type" mean properTypesCache.clear() notProperTypesCache.clear() - require(typeVariablesThatAreNotCountedAsProperTypes == null) { + require(typeVariablesThatAreCountedAsProperTypes == null) { "Currently there should be no nested withDisallowingOnlyThisTypeVariablesForProperTypes calls" } - typeVariablesThatAreNotCountedAsProperTypes = typeVariables + typeVariablesThatAreCountedAsProperTypes = typeVariables - block() + val result = block() - typeVariablesThatAreNotCountedAsProperTypes = null + typeVariablesThatAreCountedAsProperTypes = null properTypesCache.clear() notProperTypesCache.clear() + + return result } private enum class State { @@ -221,6 +224,7 @@ class NewConstraintSystemImpl( // ConstraintSystemBuilder private fun transactionRegisterVariable(variable: TypeVariableMarker) { if (state != State.TRANSACTION) return + if (variable.freshTypeConstructor() in storage.allTypeVariables) return typeVariablesTransaction.add(variable) } @@ -302,13 +306,25 @@ class NewConstraintSystemImpl( } fun addOuterSystem(outerSystem: ConstraintStorage) { - addOtherSystem(outerSystem) + require(!storage.usesOuterCs) + + storage.usesOuterCs = true storage.outerSystemVariablesPrefixSize = outerSystem.allTypeVariables.size + @OptIn(AssertionsOnly::class) + storage.outerCS = outerSystem + + addOtherSystem(outerSystem, isAddingOuter = true) } - fun setBaseSystem(outerSystem: ConstraintStorage) { - addOtherSystem(outerSystem) - storage.outerSystemVariablesPrefixSize = outerSystem.outerSystemVariablesPrefixSize + @K2Only + fun setBaseSystem(baseSystem: ConstraintStorage) { + require(storage.allTypeVariables.isEmpty()) + storage.usesOuterCs = baseSystem.usesOuterCs + storage.outerSystemVariablesPrefixSize = baseSystem.outerSystemVariablesPrefixSize + @OptIn(AssertionsOnly::class) + storage.outerCS = (baseSystem as? MutableConstraintStorage)?.outerCS + + addOtherSystem(baseSystem) } fun prepareForGlobalCompletion() { @@ -317,6 +333,17 @@ class NewConstraintSystemImpl( } override fun addOtherSystem(otherSystem: ConstraintStorage) { + addOtherSystem(otherSystem, isAddingOuter = false) + } + + fun replaceContentWith(otherSystem: ConstraintStorage) { + addOtherSystem(otherSystem, isAddingOuter = false, clearNotFixedTypeVariables = true) + } + + private fun addOtherSystem(otherSystem: ConstraintStorage, isAddingOuter: Boolean, clearNotFixedTypeVariables: Boolean = false) { + @OptIn(AssertionsOnly::class) + runOuterCSRelatedAssertions(otherSystem, isAddingOuter) + if (otherSystem.allTypeVariables.isNotEmpty()) { otherSystem.allTypeVariables.forEach { transactionRegisterVariable(it.value) @@ -324,16 +351,47 @@ class NewConstraintSystemImpl( storage.allTypeVariables.putAll(otherSystem.allTypeVariables) notProperTypesCache.clear() } + + // `clearNotFixedTypeVariables` means that we're mostly replacing the content, thus we need to remove variables that have been fixed + // in `otherSystem` from `this.notFixedTypeVariables`, too + if (clearNotFixedTypeVariables) { + notFixedTypeVariables.clear() + } + for ((variable, constraints) in otherSystem.notFixedTypeVariables) { notFixedTypeVariables[variable] = MutableVariableWithConstraints(this, constraints) } - storage.initialConstraints.addAll(otherSystem.initialConstraints) + + val currentInitialConstraints = storage.initialConstraints.toSet() + + otherSystem.initialConstraints.filterTo(storage.initialConstraints) { + it !in currentInitialConstraints + } + storage.maxTypeDepthFromInitialConstraints = max(storage.maxTypeDepthFromInitialConstraints, otherSystem.maxTypeDepthFromInitialConstraints) storage.errors.addAll(otherSystem.errors) storage.fixedTypeVariables.putAll(otherSystem.fixedTypeVariables) storage.postponedTypeVariables.addAll(otherSystem.postponedTypeVariables) storage.constraintsFromAllForkPoints.addAll(otherSystem.constraintsFromAllForkPoints) + + } + + + @AssertionsOnly + private fun runOuterCSRelatedAssertions(otherSystem: ConstraintStorage, isAddingOuter: Boolean) { + if (!otherSystem.usesOuterCs) return + + // When integrating a child system back, it's ok that for root CS, `storage.usesOuterCs == false` + if ((otherSystem as? MutableConstraintStorage)?.outerCS === storage) return + + require(storage.usesOuterCs) + + if (!isAddingOuter) { + require(storage.outerSystemVariablesPrefixSize == otherSystem.outerSystemVariablesPrefixSize) { + "Expected to be ${otherSystem.outerSystemVariablesPrefixSize}, but ${storage.outerSystemVariablesPrefixSize} found" + } + } } // ResultTypeResolver.Context, ConstraintSystemBuilder @@ -357,11 +415,11 @@ class NewConstraintSystemImpl( it if (typeToCheck == null) return@contains false - if (typeVariablesThatAreNotCountedAsProperTypes != null) { - return@contains typeVariablesThatAreNotCountedAsProperTypes!!.contains(typeToCheck.typeConstructor()) + if (typeVariablesThatAreCountedAsProperTypes?.contains(typeToCheck.typeConstructor()) == true) { + return@contains false } - storage.allTypeVariables.containsKey(typeToCheck.typeConstructor()) + return@contains storage.allTypeVariables.containsKey(typeToCheck.typeConstructor()) } override fun isTypeVariable(type: KotlinTypeMarker): Boolean { @@ -689,7 +747,7 @@ class NewConstraintSystemImpl( return buildCurrentSubstitutor(emptyMap()) } - override fun buildCurrentSubstitutor(additionalBindings: Map): TypeSubstitutorMarker { + override fun buildCurrentSubstitutor(additionalBindings: Map): TypeSubstitutorMarker { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) return storage.buildCurrentSubstitutor(this, additionalBindings) } @@ -715,6 +773,8 @@ class NewConstraintSystemImpl( return storage } + val usesOuterCs: Boolean get() = storage.usesOuterCs + // PostponedArgumentsAnalyzer.Context override fun hasUpperOrEqualUnitConstraint(type: KotlinTypeMarker): Boolean { checkState(State.BUILDING, State.COMPLETION, State.FREEZED) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt index 45740d7a621..6f2688b38a5 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt @@ -10,22 +10,25 @@ import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor -import org.jetbrains.kotlin.resolve.calls.components.candidate.ResolutionCandidate import org.jetbrains.kotlin.resolve.calls.components.candidate.CallableReferenceResolutionCandidate +import org.jetbrains.kotlin.resolve.calls.components.candidate.ResolutionCandidate import org.jetbrains.kotlin.resolve.calls.components.candidate.SimpleResolutionCandidate import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem import org.jetbrains.kotlin.resolve.calls.inference.addEqualityConstraintIfCompatible -import org.jetbrains.kotlin.resolve.calls.inference.components.* +import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode +import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter +import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutorByConstructorMap +import org.jetbrains.kotlin.resolve.calls.inference.components.TrivialConstraintTypeInferenceOracle import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.Empty.hasContradiction import org.jetbrains.kotlin.resolve.calls.inference.model.ExpectedTypeConstraintPositionImpl import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.tower.CandidateFactory import org.jetbrains.kotlin.resolve.calls.tower.forceResolution -import org.jetbrains.kotlin.types.error.ErrorUtils import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.error.ErrorType import org.jetbrains.kotlin.types.error.ErrorTypeKind +import org.jetbrains.kotlin.types.error.ErrorUtils import org.jetbrains.kotlin.types.model.safeSubstitute import org.jetbrains.kotlin.utils.addToStdlib.same @@ -80,6 +83,7 @@ class KotlinCallCompleter( candidate.runCompletion(completionMode, diagnosticHolder, resolutionCallbacks) candidate.asCallResolutionResult(completionMode, diagnosticHolder) } + ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL -> error("PCLA might be only run for K2") ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA -> throw IllegalStateException("Should not be here") } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt index 9585b4605d6..f5dbfd2804e 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt @@ -12,7 +12,10 @@ import org.jetbrains.kotlin.resolve.calls.components.transformToResolvedLambda import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.model.MultiLambdaBuilderInferenceRestriction -import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.KotlinTypeFactory +import org.jetbrains.kotlin.types.TypeConstructor +import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.error.ErrorTypeKind import org.jetbrains.kotlin.types.error.ErrorUtils import org.jetbrains.kotlin.types.model.* @@ -279,7 +282,7 @@ class KotlinConstraintSystemCompleter( // continue completion (rerun stages) only if ready for fixation variables with proper constraints have appeared // (after analysing a lambda with the builder inference) // otherwise we don't continue and report "not enough type information" error - return variableForFixation?.hasProperConstraint == true + return variableForFixation?.isReady == true } private fun transformToAtomWithNewFunctionalExpectedType( @@ -319,7 +322,7 @@ class KotlinConstraintSystemCompleter( topLevelType ) ?: return false - if (!variableForFixation.hasProperConstraint) return false + if (!variableForFixation.isReady) return false fixVariable(this, notFixedTypeVariables.getValue(variableForFixation.variable), topLevelAtoms, diagnosticsHolder) @@ -340,7 +343,7 @@ class KotlinConstraintSystemCompleter( postponedArguments, completionMode, topLevelType, ) ?: break - assert(!variableForFixation.hasProperConstraint) { + assert(!variableForFixation.isReady) { "At this stage there should be no remaining variables with proper constraints" } diff --git a/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt b/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt index 7d69e04c9a9..b0a3d4a537d 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt @@ -8,7 +8,9 @@ package org.jetbrains.kotlin.types.model import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind import org.jetbrains.kotlin.resolve.checkers.EmptyIntersectionTypeChecker import org.jetbrains.kotlin.resolve.checkers.EmptyIntersectionTypeInfo -import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.AbstractTypeChecker +import org.jetbrains.kotlin.types.TypeCheckerState +import org.jetbrains.kotlin.types.Variance import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @@ -310,6 +312,9 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui */ fun KotlinTypeMarker.convertToNonRaw(): KotlinTypeMarker + @K2Only + fun createSubstitutionFromSubtypingStubTypesToTypeVariables(): TypeSubstitutorMarker + fun createCapturedStarProjectionForSelfType( typeVariable: TypeVariableTypeConstructorMarker, typesForRecursiveTypeParameters: List, @@ -588,3 +593,6 @@ fun requireOrDescribe(condition: Boolean, value: Any?) { @RequiresOptIn("This kinds of type is obsolete and should not be used until you really need it") annotation class ObsoleteTypeKind + +@RequiresOptIn +annotation class K2Only diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt index 569c1a9f4df..e755500cbd7 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt @@ -622,6 +622,11 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy errorSupportedOnlyInTypeInference() } + @K2Only + override fun createSubstitutionFromSubtypingStubTypesToTypeVariables(): TypeSubstitutorMarker { + error("Only for K2") + } + override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker { require(type is UnwrappedType, type::errorMessage) require(this is TypeSubstitutor, this::errorMessage)