From 276f5b26d8fe742eef2d76d8da6ab8b393678e64 Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Mon, 21 Aug 2023 17:51:54 +0200 Subject: [PATCH] K2: Implement partially constrained lambda analysis (PCLA) It's expected to partially mimic the behavior of what previously was called builder inference, but with more clear contracts (documentation is in progress, though) See a lot of fixed issues in the later commits with test data, especially [red-to-green] ^KT-59791 In Progress --- .idea/dictionaries/Denis_Zharkov.xml | 7 + compiler/fir/build.gradle.kts | 1 + .../coneDiagnosticToFirDiagnostic.kt | 10 +- .../fir/types/ConeTypeVariableAndStubTypes.kt | 2 + .../kotlin/fir/resolve/ScopeUtils.kt | 7 + .../kotlin/fir/resolve/calls/FirReceivers.kt | 20 +- .../kotlin/fir/types/ConeInferenceContext.kt | 18 +- .../jetbrains/kotlin/fir/FirCallResolver.kt | 17 +- .../FirOverloadByLambdaReturnTypeResolver.kt | 23 +- .../resolve/FirSpecialTowerDataContexts.kt | 43 +- .../kotlin/fir/resolve/ResolutionMode.kt | 6 +- .../kotlin/fir/resolve/calls/Arguments.kt | 23 +- .../kotlin/fir/resolve/calls/CallKind.kt | 3 + .../kotlin/fir/resolve/calls/Candidate.kt | 24 +- .../fir/resolve/calls/CandidateFactory.kt | 37 +- .../fir/resolve/calls/ResolutionStages.kt | 50 +- .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 20 +- .../inference/ConstraintSystemCompleter.kt | 182 ++++---- .../inference/FirBuilderInferenceSession.kt | 430 ------------------ .../fir/resolve/inference/FirCallCompleter.kt | 120 ++--- .../FirDelegatedPropertyInferenceSession.kt | 181 +++++--- .../resolve/inference/FirInferenceSession.kt | 71 +-- .../FirInferenceSessionForChainedResolve.kt | 34 -- .../inference/FirPCLAInferenceSession.kt | 312 +++++++++++++ .../inference/PostponedArgumentsAnalyzer.kt | 113 +++-- ...rCallCompletionResultsWriterTransformer.kt | 83 +++- .../body/resolve/BodyResolveContext.kt | 76 +--- .../FirDeclarationsResolveTransformer.kt | 126 +++-- .../FirExpressionsResolveTransformer.kt | 22 +- .../fir/resolve/calls/ResolutionDiagnostic.kt | 9 +- .../PostponedArgumentsAnalyzerContext.kt | 5 +- .../ConstraintSystemCompletionContext.kt | 16 +- .../ConstraintSystemCompletionMode.kt | 9 +- .../PostponedArgumentInputTypesResolver.kt | 87 ++-- .../components/ResultTypeResolver.kt | 16 +- ...peVariableDependencyInformationProvider.kt | 22 +- .../components/VariableFixationFinder.kt | 66 ++- .../inference/model/ConstraintStorage.kt | 6 + .../model/MutableConstraintStorage.kt | 19 + .../model/NewConstraintSystemImpl.kt | 90 +++- .../calls/components/KotlinCallCompleter.kt | 10 +- .../KotlinConstraintSystemCompleter.kt | 11 +- .../kotlin/types/model/TypeSystemContext.kt | 10 +- .../types/checker/ClassicTypeSystemContext.kt | 5 + 44 files changed, 1356 insertions(+), 1086 deletions(-) create mode 100644 .idea/dictionaries/Denis_Zharkov.xml delete mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt delete mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSessionForChainedResolve.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirPCLAInferenceSession.kt 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)