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
This commit is contained in:
Denis.Zharkov
2023-08-21 17:51:54 +02:00
committed by Space Team
parent 9ae5287df8
commit 276f5b26d8
44 changed files with 1356 additions and 1086 deletions
+7
View File
@@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="Denis.Zharkov">
<words>
<w>pcla</w>
</words>
</dictionary>
</component>
+1
View File
@@ -22,6 +22,7 @@ subprojects {
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>().configureEach { tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>().configureEach {
kotlinOptions { kotlinOptions {
freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.fir.symbols.SymbolInternals" freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.fir.symbols.SymbolInternals"
freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.types.model.K2Only"
} }
} }
} }
@@ -316,13 +316,13 @@ private fun mapInapplicableCandidateError(
rootCause.expectedContextReceiverType.removeTypeVariableTypes(typeContext) rootCause.expectedContextReceiverType.removeTypeVariableTypes(typeContext)
) )
is StubBuilderInferenceReceiver -> { is TypeVariableAsExplicitReceiver -> {
val typeParameterSymbol = rootCause.typeParameterSymbol val typeParameter = rootCause.typeParameter
@OptIn(SymbolInternals::class) @OptIn(SymbolInternals::class)
FirErrors.BUILDER_INFERENCE_STUB_RECEIVER.createOn( FirErrors.BUILDER_INFERENCE_STUB_RECEIVER.createOn(
qualifiedAccessSource ?: source, rootCause.explicitReceiver.source,
typeParameterSymbol.name, typeParameter.symbol.name,
(typeParameterSymbol.containingDeclarationSymbol.fir as FirMemberDeclaration).nameOrSpecialName (typeParameter.symbol.containingDeclarationSymbol.fir as FirMemberDeclaration).nameOrSpecialName
) )
} }
@@ -46,6 +46,8 @@ class ConeTypeVariableTypeConstructor(
fun recordInfoAboutTypeVariableUsagesAsInvariantOrContravariantParameter() { fun recordInfoAboutTypeVariableUsagesAsInvariantOrContravariantParameter() {
isContainedInInvariantOrContravariantPositions = true isContainedInInvariantOrContravariantPositions = true
} }
override fun toString(): String = "${this::class.simpleName}($debugName)"
} }
// ----------------------------------- Stub types ----------------------------------- // ----------------------------------- Stub types -----------------------------------
@@ -125,6 +125,13 @@ private fun ConeKotlinType.scope(
is ConeDefinitelyNotNullType -> original.scope(useSiteSession, scopeSession, requiredMembersPhase) is ConeDefinitelyNotNullType -> original.scope(useSiteSession, scopeSession, requiredMembersPhase)
is ConeIntegerConstantOperatorType -> scopeSession.getOrBuildScopeForIntegerConstantOperatorType(useSiteSession, this) is ConeIntegerConstantOperatorType -> scopeSession.getOrBuildScopeForIntegerConstantOperatorType(useSiteSession, this)
is ConeIntegerLiteralConstantType -> error("ILT should not be in receiver position") 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 else -> null
} }
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.fakeElement import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase 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.FirCheckNotNullCall
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression 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.buildSmartCastExpression
import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpression import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpression
import org.jetbrains.kotlin.fir.references.builder.buildImplicitThisReference 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.ScopeSession
import org.jetbrains.kotlin.fir.resolve.scope import org.jetbrains.kotlin.fir.resolve.scope
import org.jetbrains.kotlin.fir.resolve.smartcastScope 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.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirScriptSymbol 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.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.name.Name
import org.jetbrains.kotlin.types.SmartcastStability import org.jetbrains.kotlin.types.SmartcastStability
@@ -89,6 +89,7 @@ sealed class ImplicitReceiverValue<S : FirBasedSymbol<*>>(
abstract val isContextReceiver: Boolean abstract val isContextReceiver: Boolean
// Type before smart cast
val originalType: ConeKotlinType = type val originalType: ConeKotlinType = type
var implicitScope: FirTypeScope? = var implicitScope: FirTypeScope? =
@@ -145,19 +146,6 @@ sealed class ImplicitReceiverValue<S : FirBasedSymbol<*>>(
@RequiresOptIn @RequiresOptIn
annotation class ImplicitReceiverInternals 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 * Should be called only in ImplicitReceiverStack
*/ */
@@ -14,10 +14,7 @@ import org.jetbrains.kotlin.fir.resolve.createSubstitutionForSupertype
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.*
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.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol
@@ -205,6 +202,10 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
return this.intersectedTypes.any { it.containsInternal(predicate) } return this.intersectedTypes.any { it.containsInternal(predicate) }
} }
if (this is ConeCapturedType) {
return this.constructor.projection.type?.containsInternal(predicate) == true
}
repeat(argumentsCount()) { index -> repeat(argumentsCount()) { index ->
val argument = getArgument(index) val argument = getArgument(index)
if (!argument.isStarProjection() && argument.getType().containsInternal(predicate)) return true if (!argument.isStarProjection() && argument.getType().containsInternal(predicate)) return true
@@ -358,6 +359,15 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
return ConeSubstitutor.Empty 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 { override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker {
if (this === NoSubstitutor) return type if (this === NoSubstitutor) return type
require(this is ConeSubstitutor) require(this is ConeSubstitutor)
@@ -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.TowerGroup
import org.jetbrains.kotlin.fir.resolve.calls.tower.TowerResolveManager import org.jetbrains.kotlin.fir.resolve.calls.tower.TowerResolveManager
import org.jetbrains.kotlin.fir.resolve.diagnostics.* 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.ResolvedCallableReferenceAtom
import org.jetbrains.kotlin.fir.resolve.inference.csBuilder
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer
@@ -398,10 +398,11 @@ class FirCallResolver(
} }
fun resolveCallableReference( fun resolveCallableReference(
constraintSystemBuilder: ConstraintSystemBuilder, containingCallCandidate: Candidate,
resolvedCallableReferenceAtom: ResolvedCallableReferenceAtom, resolvedCallableReferenceAtom: ResolvedCallableReferenceAtom,
hasSyntheticOuterCall: Boolean, hasSyntheticOuterCall: Boolean,
): Pair<CandidateApplicability, Boolean> { ): Pair<CandidateApplicability, Boolean> = components.context.inferenceSession.runCallableReferenceResolution(containingCallCandidate) {
val constraintSystemBuilder = containingCallCandidate.csBuilder
val callableReferenceAccess = resolvedCallableReferenceAtom.reference val callableReferenceAccess = resolvedCallableReferenceAtom.reference
val calleeReference = callableReferenceAccess.calleeReference val calleeReference = callableReferenceAccess.calleeReference
val lhs = resolvedCallableReferenceAtom.lhs val lhs = resolvedCallableReferenceAtom.lhs
@@ -450,7 +451,7 @@ class FirCallResolver(
calleeReference.source calleeReference.source
) )
resolvedCallableReferenceAtom.resultingReference = errorReference resolvedCallableReferenceAtom.resultingReference = errorReference
return applicability to false return@runCallableReferenceResolution applicability to false
} }
reducedCandidates.size > 1 -> { reducedCandidates.size > 1 -> {
if (resolvedCallableReferenceAtom.hasBeenPostponed) { if (resolvedCallableReferenceAtom.hasBeenPostponed) {
@@ -460,10 +461,10 @@ class FirCallResolver(
calleeReference.source calleeReference.source
) )
resolvedCallableReferenceAtom.resultingReference = errorReference resolvedCallableReferenceAtom.resultingReference = errorReference
return applicability to false return@runCallableReferenceResolution applicability to false
} }
resolvedCallableReferenceAtom.hasBeenPostponed = true resolvedCallableReferenceAtom.hasBeenPostponed = true
return applicability to true return@runCallableReferenceResolution applicability to true
} }
} }
@@ -486,7 +487,7 @@ class FirCallResolver(
resolvedCallableReferenceAtom.resultingReference = reference resolvedCallableReferenceAtom.resultingReference = reference
resolvedCallableReferenceAtom.resultingTypeForCallableReference = chosenCandidate.resultingTypeForCallableReference resolvedCallableReferenceAtom.resultingTypeForCallableReference = chosenCandidate.resultingTypeForCallableReference
return applicability to true return@runCallableReferenceResolution applicability to true
} }
fun resolveDelegatingConstructorCall( 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. * can be important in builder inference mode, and it will never work if we skip completion here.
* See inferenceFromLambdaReturnStatement.kt test. * See inferenceFromLambdaReturnStatement.kt test.
*/ */
if (components.context.inferenceSession !is FirBuilderInferenceSession && if (!candidate.usedOuterCs &&
createResolvedReferenceWithoutCandidateForLocalVariables && createResolvedReferenceWithoutCandidateForLocalVariables &&
explicitReceiver?.resolvedType !is ConeIntegerLiteralType && explicitReceiver?.resolvedType !is ConeIntegerLiteralType &&
coneSymbol is FirVariableSymbol && coneSymbol is FirVariableSymbol &&
@@ -39,26 +39,6 @@ class FirOverloadByLambdaReturnTypeResolver(
): Set<Candidate> where T : FirStatement, T : FirResolvable { ): Set<Candidate> where T : FirStatement, T : FirResolvable {
if (bestCandidates.size <= 1) return bestCandidates 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( return reduceCandidatesImpl(
qualifiedAccess, qualifiedAccess,
bestCandidates, bestCandidates,
@@ -137,7 +117,7 @@ class FirOverloadByLambdaReturnTypeResolver(
firstCandidate.system, firstCandidate.system,
firstAtom, firstAtom,
firstCandidate, firstCandidate,
ConstraintSystemCompletionMode.FULL, forOverloadByLambdaReturnType = true,
) )
while (iterator.hasNext()) { while (iterator.hasNext()) {
val (candidate, atom) = iterator.next() val (candidate, atom) = iterator.next()
@@ -147,7 +127,6 @@ class FirOverloadByLambdaReturnTypeResolver(
atom, atom,
candidate, candidate,
results, results,
ConstraintSystemCompletionMode.FULL,
) )
} }
@@ -7,43 +7,54 @@ package org.jetbrains.kotlin.fir.resolve
import org.jetbrains.kotlin.fir.declarations.FirTowerDataContext import org.jetbrains.kotlin.fir.declarations.FirTowerDataContext
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
import org.jetbrains.kotlin.fir.resolve.inference.FirInferenceSession
import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousFunctionSymbol
class FirSpecialTowerDataContexts { class FirSpecialTowerDataContexts {
private val towerDataContextForAnonymousFunctions: MutableMap<FirAnonymousFunctionSymbol, FirTowerDataContext> = mutableMapOf() private val contextForAnonymousFunctions: MutableMap<FirAnonymousFunctionSymbol, PostponedAtomsResolutionContext> = mutableMapOf()
private val towerDataContextForCallableReferences: MutableMap<FirCallableReferenceAccess, FirTowerDataContext> = mutableMapOf() private val contextForCallableReferences: MutableMap<FirCallableReferenceAccess, PostponedAtomsResolutionContext> = mutableMapOf()
fun getAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol): FirTowerDataContext? { fun getAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol): PostponedAtomsResolutionContext? {
return towerDataContextForAnonymousFunctions[symbol] return contextForAnonymousFunctions[symbol]
} }
fun getCallableReferenceContext(access: FirCallableReferenceAccess): FirTowerDataContext? { fun getCallableReferenceContext(access: FirCallableReferenceAccess): PostponedAtomsResolutionContext? {
return towerDataContextForCallableReferences[access] return contextForCallableReferences[access]
} }
fun storeAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol, context: FirTowerDataContext) { fun storeAnonymousFunctionContext(
towerDataContextForAnonymousFunctions[symbol] = context symbol: FirAnonymousFunctionSymbol,
context: FirTowerDataContext,
inferenceSession: FirInferenceSession,
) {
contextForAnonymousFunctions[symbol] = Pair(context, inferenceSession)
} }
fun dropAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol) { fun dropAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol) {
towerDataContextForAnonymousFunctions.remove(symbol) contextForAnonymousFunctions.remove(symbol)
} }
fun storeCallableReferenceContext(access: FirCallableReferenceAccess, context: FirTowerDataContext) { fun storeCallableReferenceContext(
towerDataContextForCallableReferences[access] = context access: FirCallableReferenceAccess,
context: FirTowerDataContext,
inferenceSession: FirInferenceSession
) {
contextForCallableReferences[access] = Pair(context, inferenceSession)
} }
fun dropCallableReferenceContext(access: FirCallableReferenceAccess) { fun dropCallableReferenceContext(access: FirCallableReferenceAccess) {
towerDataContextForCallableReferences.remove(access) contextForCallableReferences.remove(access)
} }
fun putAll(contexts: FirSpecialTowerDataContexts) { fun putAll(contexts: FirSpecialTowerDataContexts) {
towerDataContextForCallableReferences.putAll(contexts.towerDataContextForCallableReferences) contextForCallableReferences.putAll(contexts.contextForCallableReferences)
towerDataContextForAnonymousFunctions.putAll(contexts.towerDataContextForAnonymousFunctions) contextForAnonymousFunctions.putAll(contexts.contextForAnonymousFunctions)
} }
fun clear() { fun clear() {
towerDataContextForAnonymousFunctions.clear() contextForAnonymousFunctions.clear()
towerDataContextForCallableReferences.clear() contextForCallableReferences.clear()
} }
} }
typealias PostponedAtomsResolutionContext = Pair<FirTowerDataContext, FirInferenceSession>
@@ -28,7 +28,11 @@ sealed class ResolutionMode(
} }
data object ContextIndependent : ResolutionMode(forceFullCompletion = true) 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( class WithExpectedType(
val expectedTypeRef: FirResolvedTypeRef, val expectedTypeRef: FirResolvedTypeRef,
@@ -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.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext
import org.jetbrains.kotlin.types.model.safeSubstitute
import org.jetbrains.kotlin.types.model.typeConstructor import org.jetbrains.kotlin.types.model.typeConstructor
val SAM_LOOKUP_NAME = Name.special("<SAM-CONSTRUCTOR>") val SAM_LOOKUP_NAME = Name.special("<SAM-CONSTRUCTOR>")
@@ -190,7 +191,7 @@ fun Candidate.resolveSubCallArgument(
* placeholder type with value 0, but argument contains type with proper literal value * 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 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( resolvePlainArgumentType(
csBuilder, csBuilder,
argument, 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( fun Candidate.resolvePlainExpressionArgument(
csBuilder: ConstraintSystemBuilder, csBuilder: ConstraintSystemBuilder,
argument: FirExpression, argument: FirExpression,
@@ -24,6 +24,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) {
ConstraintSystemForks, ConstraintSystemForks,
CheckIncompatibleTypeVariableUpperBounds, CheckIncompatibleTypeVariableUpperBounds,
TypeParameterAsCallable, TypeParameterAsCallable,
TypeVariablesInExplicitReceivers,
) )
object SyntheticSelect : CallKind( object SyntheticSelect : CallKind(
@@ -59,6 +60,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) {
ConstraintSystemForks, ConstraintSystemForks,
CheckIncompatibleTypeVariableUpperBounds, CheckIncompatibleTypeVariableUpperBounds,
TypeParameterAsCallable, TypeParameterAsCallable,
TypeVariablesInExplicitReceivers,
) )
object DelegatingConstructorCall : CallKind( object DelegatingConstructorCall : CallKind(
@@ -94,6 +96,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) {
CheckIncompatibleTypeVariableUpperBounds, CheckIncompatibleTypeVariableUpperBounds,
ProcessDynamicExtensionAnnotation, ProcessDynamicExtensionAnnotation,
LowerPriorityIfDynamic, LowerPriorityIfDynamic,
TypeVariablesInExplicitReceivers,
) )
object SyntheticIdForCallableReferencesResolution : CallKind( object SyntheticIdForCallableReferencesResolution : CallKind(
@@ -7,12 +7,12 @@ package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.fakeElement import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirValueParameter import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression
import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpressionCopy import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpressionCopy
import org.jetbrains.kotlin.fir.expressions.impl.FirExpressionStub 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.InferenceComponents
import org.jetbrains.kotlin.fir.resolve.inference.PostponedResolvedAtom import org.jetbrains.kotlin.fir.resolve.inference.PostponedResolvedAtom
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor 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.CandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.util.CodeFragmentAdjustment import org.jetbrains.kotlin.util.CodeFragmentAdjustment
import org.jetbrains.kotlin.utils.addToStdlib.runUnless
class Candidate( class Candidate(
symbol: FirBasedSymbol<*>, symbol: FirBasedSymbol<*>,
@@ -47,6 +48,7 @@ class Candidate(
val isFromCompanionObjectTypeScope: Boolean = false, 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 // 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, val isFromOriginalTypeInPresenceOfSmartCast: Boolean = false,
inferenceSession: FirInferenceSession,
) : AbstractCandidate() { ) : AbstractCandidate() {
override var symbol: FirBasedSymbol<*> = symbol override var symbol: FirBasedSymbol<*> = symbol
@@ -66,10 +68,21 @@ class Candidate(
this.symbol = symbol this.symbol = symbol
} }
val usedOuterCs: Boolean get() = system.usesOuterCs
private var systemInitialized: Boolean = false private var systemInitialized: Boolean = false
val system: NewConstraintSystemImpl by lazy(LazyThreadSafetyMode.NONE) { val system: NewConstraintSystemImpl by lazy(LazyThreadSafetyMode.NONE) {
val system = constraintSystemFactory.createConstraintSystem() 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 systemInitialized = true
system system
} }
@@ -102,6 +115,9 @@ class Candidate(
var functionTypesOfSamConversions: HashMap<FirExpression, ConeKotlinType>? = null var functionTypesOfSamConversions: HashMap<FirExpression, ConeKotlinType>? = null
lateinit var typeArgumentMapping: TypeArgumentMapping lateinit var typeArgumentMapping: TypeArgumentMapping
val postponedAtoms = mutableListOf<PostponedResolvedAtom>() val postponedAtoms = mutableListOf<PostponedResolvedAtom>()
val postponedPCLACalls = mutableListOf<FirStatement>()
val lambdasAnalyzedWithPCLA = mutableListOf<FirAnonymousFunction>()
val onCompletionResultsWritingCallbacks = mutableListOf<(ConeSubstitutor) -> Unit>()
var currentApplicability = CandidateApplicability.RESOLVED var currentApplicability = CandidateApplicability.RESOLVED
private set private set
@@ -35,7 +35,6 @@ class CandidateFactory private constructor(
companion object { companion object {
private fun buildBaseSystem(context: ResolutionContext, callInfo: CallInfo): ConstraintStorage { private fun buildBaseSystem(context: ResolutionContext, callInfo: CallInfo): ConstraintStorage {
val system = context.inferenceComponents.createConstraintSystem() val system = context.inferenceComponents.createConstraintSystem()
system.addOuterSystem(context.bodyResolveContext.outerConstraintStorage)
callInfo.arguments.forEach { callInfo.arguments.forEach {
system.addSubsystemFromExpression(it) system.addSubsystemFromExpression(it)
} }
@@ -79,6 +78,7 @@ class CandidateFactory private constructor(
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> false ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> false
}, },
isFromOriginalTypeInPresenceOfSmartCast, isFromOriginalTypeInPresenceOfSmartCast,
context.bodyResolveContext.inferenceSession,
) )
// The counterpart in FE 1.0 checks if the given descriptor is VariableDescriptor yet not PropertyDescriptor. // 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, baseSystem,
callInfo, callInfo,
originScope = null, 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) { return when (statement) {
is FirQualifiedAccessExpression, is FirQualifiedAccessExpression,
is FirWhenExpression, is FirWhenExpression,
is FirTryExpression, is FirTryExpression,
is FirCheckNotNullCall, is FirCheckNotNullCall,
is FirElvisExpression -> { is FirElvisExpression,
-> {
val candidate = (statement as FirResolvable).candidate() ?: return false val candidate = (statement as FirResolvable).candidate() ?: return false
addOtherSystem(candidate.system.asReadOnlyStorage()) processor(candidate.system.asReadOnlyStorage())
true true
} }
is FirSafeCallExpression -> addSubsystemFromExpression(statement.selector) is FirSafeCallExpression -> processConstraintStorageFromExpression(statement.selector, processor)
is FirWrappedArgumentExpression -> addSubsystemFromExpression(statement.expression) is FirWrappedArgumentExpression -> processConstraintStorageFromExpression(statement.expression, processor)
is FirBlock -> statement.returnExpressions().any { addSubsystemFromExpression(it) } 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 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? { internal fun FirResolvable.candidate(): Candidate? {
return when (val callee = this.calleeReference) { return when (val callee = this.calleeReference) {
is FirNamedReferenceWithCandidate -> return callee.candidate is FirNamedReferenceWithCandidate -> return callee.candidate
@@ -123,21 +123,6 @@ object CheckExtensionReceiver : ResolutionStage() {
candidate.chosenExtensionReceiver = receiver.expression 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() 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( private fun Candidate.findClosestMatchingReceivers(
expectedType: ConeKotlinType, expectedType: ConeKotlinType,
receiverGroups: List<List<FirExpression>>, receiverGroups: List<List<FirExpression>>,
@@ -569,7 +587,7 @@ internal object EagerResolveOfCallableReferences : CheckerStage() {
if (atom is ResolvedCallableReferenceAtom) { if (atom is ResolvedCallableReferenceAtom) {
val (applicability, success) = val (applicability, success) =
context.bodyResolveComponents.callResolver.resolveCallableReference( 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 (!success) {
// If the resolution was unsuccessful, we ensure that an error will be reported for the callable reference // If the resolution was unsuccessful, we ensure that an error will be reported for the callable reference
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.references.symbol
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue 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.dfa.cfg.*
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap 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.impl.toConeType
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol 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.fir.types.*
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.name.StandardClassIds
@@ -926,6 +930,11 @@ abstract class FirDataFlowAnalyzer(
private fun FirStatement.orderedArguments(callee: FirFunction): Array<out FirExpression?>? { private fun FirStatement.orderedArguments(callee: FirFunction): Array<out FirExpression?>? {
fun FirQualifiedAccessExpression.firstReceiver(): FirExpression? { 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 return extensionReceiver ?: dispatchReceiver
} }
@@ -937,7 +946,8 @@ abstract class FirDataFlowAnalyzer(
return when (this) { return when (this) {
is FirFunctionCall -> { 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() } val parameterToArgument = argumentToParameter.entries.associate { it.value to it.key.unwrapArgument() }
Array(callee.valueParameters.size + 1) { i -> Array(callee.valueParameters.size + 1) { i ->
if (i > 0) parameterToArgument[callee.valueParameters[i - 1]] else receiver if (i > 0) parameterToArgument[callee.valueParameters[i - 1]] else receiver
@@ -1155,7 +1165,11 @@ abstract class FirDataFlowAnalyzer(
} }
private fun exitBooleanNot(flow: MutableFlow, expression: FirFunctionCall) { 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) val expressionVariable = variableStorage.createSynthetic(expression)
// Alternatively: (expression == true => argument == false) && (expression == false => argument == true) // Alternatively: (expression == true => argument == false) && (expression == false => argument == true)
// Which implementation is faster and/or consumes less memory is an open question. // Which implementation is faster and/or consumes less memory is an open question.
@@ -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.ConeErrorType
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeVariable import org.jetbrains.kotlin.fir.types.ConeTypeVariable
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionContext 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.TypeVariableDependencyInformationProvider
import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl 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.NotEnoughInformationForTypeParameter
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints 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)) if (analyzeArgumentWithFixedParameterTypes(languageVersionSettings, postponedArguments, analyze))
continue continue
val isThereAnyReadyForFixationVariable = variableFixationFinder.findFirstVariableForFixation( val isThereAnyReadyForFixationVariable = findFirstVariableForFixation(
this, collectVariablesFromContext,
getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms), topLevelAtoms,
postponedArguments, postponedArguments,
completionMode, completionMode,
topLevelType topLevelType
@@ -113,7 +110,7 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c
if (wasBuiltNewExpectedTypeForSomeArgument) if (wasBuiltNewExpectedTypeForSomeArgument)
continue continue
if (completionMode == ConstraintSystemCompletionMode.FULL) { if (completionMode.allLambdasShouldBeAnalyzed) {
// Stage 3: fix variables for parameter types of all postponed arguments // Stage 3: fix variables for parameter types of all postponed arguments
for (argument in postponedArguments) { for (argument in postponedArguments) {
val variableWasFixed = postponedArgumentsInputTypesResolver.fixNextReadyVariableForParameterTypeIfNeeded( val variableWasFixed = postponedArgumentsInputTypesResolver.fixNextReadyVariableForParameterTypeIfNeeded(
@@ -152,21 +149,33 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c
continue continue
// Stage 7: try to complete call with the builder inference if there are uninferred type variables // Stage 7: try to complete call with the builder inference if there are uninferred type variables
val allTypeVariables = getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms) val areThereAppearedProperConstraintsForSomeVariable = tryToCompleteWithPCLA(
val areThereAppearedProperConstraintsForSomeVariable = tryToCompleteWithBuilderInference( completionMode, postponedArguments, analyze,
completionMode, topLevelType, postponedArguments, allTypeVariables, analyze
) )
if (areThereAppearedProperConstraintsForSomeVariable) if (areThereAppearedProperConstraintsForSomeVariable)
continue 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 // Stage 8: report "not enough information" for uninferred type variables
reportNotEnoughTypeInformation( 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 // 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)) if (analyzeRemainingNotAnalyzedPostponedArgument(postponedArguments, analyze))
continue continue
} }
@@ -175,20 +184,61 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c
} }
} }
private fun ConstraintSystemCompletionContext.reportNotEnoughInformationForTypeVariablesRequiredForInputTypesOfLambdas(
postponedArguments: List<PostponedResolvedAtom>,
topLevelType: ConeKotlinType,
dependencyProvider: TypeVariableDependencyInformationProvider,
topLevelAtoms: List<FirStatement>,
) {
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<FirStatement>,
postponedArguments: List<PostponedResolvedAtom>,
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` * 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 * 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 * @return true if we got new proper constraints after builder inference
*/ */
private fun ConstraintSystemCompletionContext.tryToCompleteWithBuilderInference( private fun ConstraintSystemCompletionContext.tryToCompleteWithPCLA(
completionMode: ConstraintSystemCompletionMode, completionMode: ConstraintSystemCompletionMode,
topLevelType: ConeKotlinType,
postponedArguments: List<PostponedResolvedAtom>, postponedArguments: List<PostponedResolvedAtom>,
allTypeVariables: List<TypeConstructorMarker>,
analyze: (PostponedResolvedAtom) -> Unit, analyze: (PostponedResolvedAtom) -> Unit,
): Boolean { ): 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 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 if (!languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceOnlyIfNeeded)) return false
@@ -198,56 +248,17 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c
fun ResolvedLambdaAtom.notFixedInputTypeVariables(): List<TypeVariableTypeConstructorMarker> = fun ResolvedLambdaAtom.notFixedInputTypeVariables(): List<TypeVariableTypeConstructorMarker> =
inputTypes.flatMap { it.extractTypeVariables() }.filter { it !in fixedTypeVariables } inputTypes.flatMap { it.extractTypeVariables() }.filter { it !in fixedTypeVariables }
val checkForDangerousBuilderInference = var anyAnalyzed = false
!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<T>.() -> 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()
for (argument in lambdaArguments) { for (argument in lambdaArguments) {
val reallyHasBuilderInferenceAnnotation = argument.isCorrespondingParameterAnnotatedWithBuilderInference
val notFixedInputTypeVariables = argument.notFixedInputTypeVariables() val notFixedInputTypeVariables = argument.notFixedInputTypeVariables()
if (notFixedInputTypeVariables.isEmpty()) continue 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) analyze(argument)
anyAnalyzed = true
} }
val variableForFixation = variableFixationFinder.findFirstVariableForFixation( return anyAnalyzed
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
} }
private fun transformToAtomWithNewFunctionExpectedType( private fun transformToAtomWithNewFunctionExpectedType(
@@ -277,16 +288,12 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c
collectVariablesFromContext: Boolean, collectVariablesFromContext: Boolean,
postponedArguments: List<PostponedResolvedAtom>, postponedArguments: List<PostponedResolvedAtom>,
): Boolean { ): Boolean {
val variableForFixation = variableFixationFinder.findFirstVariableForFixation( val variableForFixation = findFirstVariableForFixation(
this, collectVariablesFromContext, topLevelAtoms, postponedArguments, completionMode, topLevelType
getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms),
postponedArguments,
completionMode,
topLevelType
) ?: return false ) ?: return false
val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable)
if (!variableForFixation.hasProperConstraint) return false if (!variableForFixation.isReady) return false
fixVariable(this, variableWithConstraints) fixVariable(this, variableWithConstraints)
@@ -297,19 +304,17 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c
completionMode: ConstraintSystemCompletionMode, completionMode: ConstraintSystemCompletionMode,
topLevelAtoms: List<FirStatement>, topLevelAtoms: List<FirStatement>,
topLevelType: ConeKotlinType, topLevelType: ConeKotlinType,
allTypeVariables: List<TypeConstructorMarker>,
postponedArguments: List<PostponedResolvedAtom>, postponedArguments: List<PostponedResolvedAtom>,
) { ) {
while (true) { while (true) {
val variableForFixation = variableFixationFinder.findFirstVariableForFixation( val variableForFixation =
this, allTypeVariables, postponedArguments, completionMode, topLevelType, findFirstVariableForFixation(false, topLevelAtoms, postponedArguments, completionMode, topLevelType)
) ?: break ?: break
assert(!variableForFixation.isReady) {
assert(!variableForFixation.hasProperConstraint) {
"At this stage there should be no remaining variables with proper constraints" "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) val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable)
processVariableWhenNotEnoughInformation(variableWithConstraints, topLevelAtoms) processVariableWhenNotEnoughInformation(variableWithConstraints, topLevelAtoms)
@@ -399,30 +404,9 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c
topLevelAtom.collectAllTypeVariables() topLevelAtom.collectAllTypeVariables()
} }
checkNotFixedTypeVariablesCountConsistency(result)
return result.toList() return result.toList()
} }
private fun ConstraintSystemCompletionContext.checkNotFixedTypeVariablesCountConsistency(
result: LinkedHashSet<TypeConstructorMarker>,
) {
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( private fun fixVariable(
c: ConstraintSystemCompletionContext, c: ConstraintSystemCompletionContext,
variableWithConstraints: VariableWithConstraints, variableWithConstraints: VariableWithConstraints,
@@ -581,13 +565,21 @@ private fun FirResolvable.processCandidateIfApplicable(
val candidate = (calleeReference as? FirNamedReferenceWithCandidate)?.candidate ?: return val candidate = (calleeReference as? FirNamedReferenceWithCandidate)?.candidate ?: return
processor(candidate) processor(candidate)
val visited = mutableSetOf<FirStatement>()
for (atom in candidate.postponedAtoms) { for (atom in candidate.postponedAtoms) {
if (atom !is ResolvedLambdaAtom || !atom.analyzed) continue if (atom !is ResolvedLambdaAtom || !atom.analyzed) continue
atom.returnStatements.forEach { atom.returnStatements.forEach {
visited += it
it.processAllContainingCallCandidates(processBlocks, processor) 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() val Candidate.csBuilder: NewConstraintSystemImpl get() = system.getBuilder()
@@ -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<ConeTypeVariable, ConeStubType>,
) : FirInferenceSessionForChainedResolve(resolutionContext) {
private val session = resolutionContext.session
private val commonCalls: MutableList<Pair<FirStatement, Candidate>> = mutableListOf()
private var lambdaImplicitReceivers: MutableList<ImplicitExtensionReceiverValue> = mutableListOf()
override fun <T> 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 <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {
if (skipCall(call)) return
commonCalls += call to candidate
}
@Suppress("UNUSED_PARAMETER")
private fun <T> 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<ConeTypeVariableTypeConstructor, ConeKotlinType>? {
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<ConeTypeVariableTypeConstructor, ConeKotlinType>
}
private fun buildCommonSystem(initialStorage: ConstraintStorage): Pair<NewConstraintSystemImpl, Boolean> {
// 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<TypeConstructorMarker, ConeKotlinType>()
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<ConeCapturedType> {
val extractedCapturedTypes = mutableSetOf<ConeCapturedType>().also { extractCapturedTypesTo(lower, it) }
return extractedCapturedTypes.filter { capturedType ->
upper.contains { it is ConeCapturedType && it.constructor === capturedType.constructor }
}
}
private fun extractCapturedTypesTo(type: ConeKotlinType, to: MutableSet<ConeCapturedType>) {
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<Stub(Tv)> <: B<Stub(Ov)> -> A<Tv> <: B<Ov>
* ```
*
* 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<CapturedType(out B)_0>, b = D<CapturedType(out B)_0>
// commonCapTypes = [CapturedType(out B)_0]
// capTypesSubstitutor = { CapturedTypeConstructor_0 => CapturedType(out W)_1 }
// substitutedA = C<CapturedType(out W)_1>
// substitutedB = D<CapturedType(out W)_1>
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<Nothing?>() {
override fun <E : FirElement> transformElement(element: E, data: Nothing?): E {
// All resolvable nodes should be implemented separately to cover substitution of receivers in the candidate
if (element is FirResolvable) {
element.candidate()?.let { processCandidate(it) }
}
// 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)
@@ -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.addSubtypeConstraintIfCompatible
import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode 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.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.types.model.safeSubstitute
import org.jetbrains.kotlin.utils.addToStdlib.runIf import org.jetbrains.kotlin.utils.addToStdlib.runIf
@@ -80,18 +79,11 @@ class FirCallCompleter(
val completionMode = candidate.computeCompletionMode( val completionMode = candidate.computeCompletionMode(
session.inferenceComponents, resolutionMode, initialType session.inferenceComponents, resolutionMode, initialType
).let { ).let {
// The difference between `shouldAvoidFullCompletion` and `!shouldRunCompletion` is very subtle: when {
// we don't run even partial completion for `!inferenceSession.shouldRunCompletion(call)`, while actually it == ConstraintSystemCompletionMode.FULL ->
// do that for `shouldAvoidFullCompletion` inferenceSession.customCompletionModeInsteadOfFull(call) ?: ConstraintSystemCompletionMode.FULL
// else -> it
// 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
} }
val analyzer = createPostponedArgumentsAnalyzer(transformer.resolutionContext) val analyzer = createPostponedArgumentsAnalyzer(transformer.resolutionContext)
@@ -101,35 +93,27 @@ class FirCallCompleter(
return when (completionMode) { return when (completionMode) {
ConstraintSystemCompletionMode.FULL -> { ConstraintSystemCompletionMode.FULL -> {
if (inferenceSession.shouldRunCompletion(call)) { runCompletionForCall(candidate, completionMode, call, initialType, analyzer)
runCompletionForCall(candidate, completionMode, call, initialType, analyzer) val finalSubstitutor = candidate.system.asReadOnlyStorage()
val finalSubstitutor = candidate.system.asReadOnlyStorage() .buildAbstractResultingSubstitutor(session.typeContext) as ConeSubstitutor
.buildAbstractResultingSubstitutor(session.typeContext) as ConeSubstitutor call.transformSingle(
val completedCall = call.transformSingle( FirCallCompletionResultsWriterTransformer(
FirCallCompletionResultsWriterTransformer( session, components.scopeSession, finalSubstitutor,
session, components.scopeSession, finalSubstitutor, components.returnTypeCalculator,
components.returnTypeCalculator, session.typeApproximator,
session.typeApproximator, components.dataFlowAnalyzer,
components.dataFlowAnalyzer, components.integerLiteralAndOperatorApproximationTransformer,
components.integerLiteralAndOperatorApproximationTransformer, components.samResolver,
components.samResolver, components.context,
components.context, ),
), null
null )
)
inferenceSession.addCompletedCall(completedCall, candidate)
completedCall
} else {
inferenceSession.processPartiallyResolvedCall(call, resolutionMode)
call
}
} }
ConstraintSystemCompletionMode.PARTIAL -> { ConstraintSystemCompletionMode.PARTIAL, ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL -> {
runCompletionForCall(candidate, completionMode, call, initialType, analyzer) runCompletionForCall(candidate, completionMode, call, initialType, analyzer)
if (inferenceSession is FirDelegatedPropertyInferenceSession) {
inferenceSession.processPartiallyResolvedCall(call, resolutionMode) inferenceSession.processPartiallyResolvedCall(call, resolutionMode, completionMode)
}
call call
} }
@@ -183,8 +167,8 @@ class FirCallCompleter(
completionMode: ConstraintSystemCompletionMode, completionMode: ConstraintSystemCompletionMode,
call: T, call: T,
initialType: ConeKotlinType, initialType: ConeKotlinType,
analyzer: PostponedArgumentsAnalyzer? = null analyzer: PostponedArgumentsAnalyzer? = null,
) where T : FirResolvable, T : FirStatement { ) where T : FirStatement {
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
val analyzer = analyzer ?: createPostponedArgumentsAnalyzer(transformer.resolutionContext) val analyzer = analyzer ?: createPostponedArgumentsAnalyzer(transformer.resolutionContext)
completer.complete( completer.complete(
@@ -194,7 +178,7 @@ class FirCallCompleter(
initialType, initialType,
transformer.resolutionContext transformer.resolutionContext
) { ) {
analyzer.analyze(candidate.system, it, candidate, completionMode) analyzer.analyze(candidate.system, it, candidate)
} }
} }
@@ -251,8 +235,9 @@ class FirCallCompleter(
contextReceivers: List<ConeKotlinType>, contextReceivers: List<ConeKotlinType>,
parameters: List<ConeKotlinType>, parameters: List<ConeKotlinType>,
expectedReturnType: ConeKotlinType?, expectedReturnType: ConeKotlinType?,
stubsForPostponedVariables: Map<TypeVariableMarker, StubTypeMarker>, candidate: Candidate,
candidate: Candidate withPCLASession: Boolean,
forOverloadByLambdaReturnType: Boolean,
): ReturnArgumentsAnalysisResult { ): ReturnArgumentsAnalysisResult {
val lambdaArgument: FirAnonymousFunction = lambdaAtom.atom val lambdaArgument: FirAnonymousFunction = lambdaAtom.atom
val needItParam = lambdaArgument.valueParameters.isEmpty() && parameters.size == 1 val needItParam = lambdaArgument.valueParameters.isEmpty() && parameters.size == 1
@@ -355,29 +340,34 @@ class FirCallCompleter(
} ?: components.noExpectedType } ?: components.noExpectedType
) )
val builderInferenceSession = runIf(stubsForPostponedVariables.isNotEmpty()) { var additionalConstraints: ConstraintStorage? = null
@Suppress("UNCHECKED_CAST")
FirBuilderInferenceSession(
lambdaArgument,
transformer.resolutionContext,
stubsForPostponedVariables as Map<ConeTypeVariable, ConeStubType>
)
}
transformer.context.withAnonymousFunctionTowerDataContext(lambdaArgument.symbol) { transformer.context.withAnonymousFunctionTowerDataContext(lambdaArgument.symbol) {
if (builderInferenceSession != null) { val pclaInferenceSession =
transformer.context.withInferenceSession(builderInferenceSession) { 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)) lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef))
applyResultsToMainCandidate()
} }
} else { } else {
lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef)) additionalConstraints =
transformer.context.inferenceSession.runLambdaCompletion(candidate, forOverloadByLambdaReturnType) {
lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef))
}
} }
} }
transformer.context.dropContextForAnonymousFunction(lambdaArgument) transformer.context.dropContextForAnonymousFunction(lambdaArgument)
val returnArguments = components.dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(lambdaArgument).map { it.expression } 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 // We only run lambda completion from ConstraintSystemCompletionContext.analyzeRemainingNotAnalyzedPostponedArgument when they are
// left uninferred. // left uninferred.
// Currently, we use stub types for builder inference, so CANNOT_INFER_PARAMETER_TYPE is the only possible result here. // 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() val diagnostic = valueParameter?.let(::ConeCannotInferValueParameterType) ?: ConeCannotInferReceiverParameterType()
return ConeErrorType(diagnostic) return ConeErrorType(diagnostic)
} }
@@ -394,6 +384,20 @@ class FirCallCompleter(
this, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference this, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference
) ?: this ) ?: 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 { private fun Candidate.isFunctionForExpectTypeFromCastFeature(): Boolean {
@@ -5,55 +5,72 @@
package org.jetbrains.kotlin.fir.resolve.inference package org.jetbrains.kotlin.fir.resolve.inference
import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.FirFunctionCallOrigin import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.expressions.FirResolvable import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.resolve.calls.InferenceError import org.jetbrains.kotlin.fir.resolve.calls.InferenceError
import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext
import org.jetbrains.kotlin.fir.resolve.calls.candidate 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.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.FirCallCompletionResultsWriterTransformer
import org.jetbrains.kotlin.fir.types.ConeClassLikeType 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.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.buildAbstractResultingSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode 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.ConstraintStorage
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addIfNotNull
class FirDelegatedPropertyInferenceSession( class FirDelegatedPropertyInferenceSession(
val property: FirProperty, private val resolutionContext: ResolutionContext,
resolutionContext: ResolutionContext, private val callCompleter: FirCallCompleter,
private val postponedArgumentsAnalyzer: PostponedArgumentsAnalyzer, private val delegateExpression: FirExpression,
) : FirInferenceSessionForChainedResolve(resolutionContext) { ) : FirInferenceSession() {
private val partiallyResolvedCalls: MutableList<Pair<FirResolvable, Candidate>> = 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() val currentConstraintStorage: ConstraintStorage get() = currentConstraintSystem.currentStorage()
private val unitType: ConeClassLikeType = components.session.builtinTypes.unitType.type private val unitType: ConeClassLikeType = components.session.builtinTypes.unitType.type
// TODO after PCLA (KT-59107): private var wasCompletionRun = false
// Outer system seems to be a property of a concrete call resolution and probably should be applied to concrete call resolution
override fun <R> onCandidatesResolution(call: FirFunctionCall, candidatesResolutionCallback: () -> R): R { override fun customCompletionModeInsteadOfFull(call: FirResolvable): ConstraintSystemCompletionMode? = when {
return if (!call.isAnyOfDelegateOperators()) call.isAnyOfDelegateOperators() && !wasCompletionRun -> ConstraintSystemCompletionMode.PARTIAL
candidatesResolutionCallback() else -> null
else
resolutionContext.bodyResolveContext.withOuterConstraintStorage(
currentConstraintSystem.currentStorage(),
candidatesResolutionCallback
)
} }
override fun <T> shouldAvoidFullCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = override fun <T> processPartiallyResolvedCall(
call.isAnyOfDelegateOperators() call: T,
resolutionMode: ResolutionMode,
completionMode: ConstraintSystemCompletionMode,
) where T : FirResolvable, T : FirStatement {
if (wasCompletionRun || !call.isAnyOfDelegateOperators()) return
override fun <T> processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement { requireCallIsDelegateOperator(call)
if (resolutionMode != ResolutionMode.ContextDependent.Delegate && !call.isAnyOfDelegateOperators()) return
val candidate = call.candidate val candidate = call.candidate
@@ -64,38 +81,84 @@ class FirDelegatedPropertyInferenceSession(
val candidateSystem = candidate.system val candidateSystem = candidate.system
partiallyResolvedCalls.add(call to candidate) partiallyResolvedCalls.add(call to candidate)
currentConstraintSystem = candidateSystem currentConstraintSystem.addOtherSystem(candidateSystem.currentStorage())
}
private fun <T> requireCallIsDelegateOperator(call: T) where T : FirResolvable, T : FirStatement {
require(call.isAnyOfDelegateOperators()) {
"Unexpected ${call.render()} call"
}
} }
private fun <T> T.isProvideDelegate() where T : FirResolvable, T : FirStatement = private fun <T> 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> T.isAnyOfDelegateOperators(): Boolean where T : FirResolvable { override fun baseConstraintStorageForCandidate(candidate: Candidate): ConstraintStorage? {
if (this !is FirFunctionCall || origin != FirFunctionCallOrigin.Operator) return false if (wasCompletionRun || !candidate.callInfo.callSite.isAnyOfDelegateOperators()) return null
val name = calleeReference.name return currentConstraintStorage
return name == OperatorNameConventions.PROVIDE_DELEGATE || name == OperatorNameConventions.GET_VALUE || name == OperatorNameConventions.SET_VALUE
} }
override fun inferPostponedVariables( fun completeSessionOrPostponeIfNonRoot(onCompletionResultsWriting: (ConeSubstitutor) -> Unit) {
lambda: ResolvedLambdaAtom, check(!wasCompletionRun)
constraintSystemBuilder: ConstraintSystemBuilder, wasCompletionRun = true
completionMode: ConstraintSystemCompletionMode,
candidate: Candidate
): Map<ConeTypeVariableTypeConstructor, ConeKotlinType>? = null
fun completeCandidates(): List<FirResolvable> { parentConstraintSystem.addOtherSystem(currentConstraintStorage)
val commonSystem = currentConstraintSystem.apply { prepareForGlobalCompletion() }
val notCompletedCalls = partiallyResolvedCalls.mapNotNull { partiallyResolvedCall -> (nonTrivialParentSession as? FirPCLAInferenceSession)?.apply {
partiallyResolvedCall.first.takeIf { resolvable -> if (delegateCandidate != null) {
resolvable.candidate() != 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<FirResolvable> {
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) { resolutionContext.bodyResolveContext.withInferenceSession(DEFAULT) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
components.callCompleter.completer.complete( components.callCompleter.completer.complete(
commonSystem.asConstraintSystemCompleterContext(), parentSystem.asConstraintSystemCompleterContext(),
ConstraintSystemCompletionMode.FULL, ConstraintSystemCompletionMode.FULL,
notCompletedCalls as List<FirStatement>, notCompletedCalls as List<FirStatement>,
unitType, resolutionContext unitType, resolutionContext
@@ -110,29 +173,31 @@ class FirDelegatedPropertyInferenceSession(
} }
found found
}.candidate }.candidate
postponedArgumentsAnalyzer.analyze( callCompleter.createPostponedArgumentsAnalyzer(resolutionContext).analyze(
commonSystem, parentSystem,
lambdaAtom, lambdaAtom,
containingCandidateForLambda, containingCandidateForLambda,
ConstraintSystemCompletionMode.FULL,
) )
} }
} }
for ((_, candidate) in partiallyResolvedCalls) { for (candidate in notCompletedCalls.mapNotNull { it.candidate() }) {
for (error in commonSystem.errors) { for (error in parentSystem.errors) {
candidate.addDiagnostic(InferenceError(error)) candidate.addDiagnostic(InferenceError(error))
} }
} }
return notCompletedCalls return notCompletedCalls
} }
}
fun createFinalSubstitutor(): ConeSubstitutor =
currentConstraintSystem.asReadOnlyStorage() fun FirElement.isAnyOfDelegateOperators(): Boolean {
.buildAbstractResultingSubstitutor(components.session.typeContext) as ConeSubstitutor if (this is FirPropertyAccessExpression) {
val originalCall = this.candidate()?.callInfo?.callSite as? FirFunctionCall ?: return false
override fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {} return originalCall.isAnyOfDelegateOperators()
}
override fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true
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
} }
@@ -5,52 +5,57 @@
package org.jetbrains.kotlin.fir.resolve.inference 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.FirResolvable
import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.types.ConeKotlinType 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.components.ConstraintSystemCompletionMode
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
abstract class FirInferenceSession { abstract class FirInferenceSession {
companion object { companion object {
val DEFAULT: FirInferenceSession = object : FirStubInferenceSession() {} val DEFAULT: FirInferenceSession = object : FirInferenceSession() {
override fun <T> 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 <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement open fun runLambdaCompletion(candidate: Candidate, forOverloadByLambdaReturnType: Boolean, block: () -> Unit): ConstraintStorage? {
block()
return null
}
/** open fun <T> runCallableReferenceResolution(candidate: Candidate, block: () -> T): T = block()
* 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 <T> shouldAvoidFullCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = false
abstract fun <T> processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement open fun customCompletionModeInsteadOfFull(
abstract fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement call: FirResolvable,
): ConstraintSystemCompletionMode? = null
abstract fun inferPostponedVariables( abstract fun <T> processPartiallyResolvedCall(
lambda: ResolvedLambdaAtom, call: T,
constraintSystemBuilder: ConstraintSystemBuilder, resolutionMode: ResolutionMode,
completionMode: ConstraintSystemCompletionMode, completionMode: ConstraintSystemCompletionMode
candidate: Candidate ) where T : FirResolvable, T : FirStatement
): Map<ConeTypeVariableTypeConstructor, ConeKotlinType>?
open fun <R> onCandidatesResolution(call: FirFunctionCall, candidatesResolutionCallback: () -> R) = candidatesResolutionCallback() open fun baseConstraintStorageForCandidate(candidate: Candidate): ConstraintStorage? = null
}
open fun addSubtypeConstraintIfCompatible(lowerType: ConeKotlinType, upperType: ConeKotlinType, element: FirElement) {}
abstract class FirStubInferenceSession : FirInferenceSession() {
override fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true
override fun <T> processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement {}
override fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {}
override fun inferPostponedVariables(
lambda: ResolvedLambdaAtom,
constraintSystemBuilder: ConstraintSystemBuilder,
completionMode: ConstraintSystemCompletionMode,
candidate: Candidate
): Map<ConeTypeVariableTypeConstructor, ConeKotlinType>? = 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<Pair<FirResolvable, Candidate>> = mutableListOf()
protected val components: BodyResolveComponents
get() = resolutionContext.bodyResolveComponents
override fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {
// do nothing
}
override fun <T> processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement {
partiallyResolvedCalls += call to call.candidate
}
protected val FirResolvable.candidate: Candidate
get() = candidate()!!
}
@@ -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 <T> 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 <T> 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 <T> 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<FirStatement>,
childStorage: ConstraintStorage,
onCompletionResultsWriting: (ConeSubstitutor) -> Unit,
) {
outerCandidate.postponedPCLACalls += childCalls
currentCommonSystem.addOtherSystem(childStorage)
outerCandidate.onCompletionResultsWritingCallbacks += onCompletionResultsWriting
}
private fun FirExpression.updateReturnTypeWithCurrentSubstitutor(
resolutionMode: ResolutionMode,
) {
val additionalBindings = mutableMapOf<TypeConstructorMarker, ConeKotlinType>()
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<ConeTypeVariableTypeConstructor, ConeKotlinType>? {
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<ConeTypeVariableTypeConstructor, ConeKotlinType>? {
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<Nothing?>() {
override fun <E : FirElement> transformElement(element: E, data: Nothing?): E {
// All resolvable nodes should be implemented separately to cover substitution of receivers in the candidate
if (element is FirResolvable) {
element.candidate()?.let { processCandidate(it) }
}
// 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) }
}
}
@@ -19,13 +19,16 @@ import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzerContext import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzerContext
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
import org.jetbrains.kotlin.resolve.calls.inference.model.BuilderInferencePosition import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
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.safeSubstitute
data class ReturnArgumentsAnalysisResult( data class ReturnArgumentsAnalysisResult(
val returnArguments: Collection<FirExpression>, val returnArguments: Collection<FirExpression>,
val inferenceSession: FirInferenceSession? val additionalConstraints: ConstraintStorage?,
) )
interface LambdaAnalyzer { interface LambdaAnalyzer {
@@ -35,8 +38,9 @@ interface LambdaAnalyzer {
contextReceivers: List<ConeKotlinType>, contextReceivers: List<ConeKotlinType>,
parameters: List<ConeKotlinType>, parameters: List<ConeKotlinType>,
expectedReturnType: ConeKotlinType?, // null means, that return type is not proper i.e. it depends on some type variables expectedReturnType: ConeKotlinType?, // null means, that return type is not proper i.e. it depends on some type variables
stubsForPostponedVariables: Map<TypeVariableMarker, StubTypeMarker>, candidate: Candidate,
candidate: Candidate withPCLASession: Boolean,
forOverloadByLambdaReturnType: Boolean,
): ReturnArgumentsAnalysisResult ): ReturnArgumentsAnalysisResult
} }
@@ -44,21 +48,24 @@ class PostponedArgumentsAnalyzer(
private val resolutionContext: ResolutionContext, private val resolutionContext: ResolutionContext,
private val lambdaAnalyzer: LambdaAnalyzer, private val lambdaAnalyzer: LambdaAnalyzer,
private val components: InferenceComponents, private val components: InferenceComponents,
private val callResolver: FirCallResolver private val callResolver: FirCallResolver,
) { ) {
fun analyze( fun analyze(
c: PostponedArgumentsAnalyzerContext, c: PostponedArgumentsAnalyzerContext,
argument: PostponedResolvedAtom, argument: PostponedResolvedAtom,
candidate: Candidate, candidate: Candidate,
completionMode: ConstraintSystemCompletionMode
) { ) {
when (argument) { when (argument) {
is ResolvedLambdaAtom -> is ResolvedLambdaAtom ->
analyzeLambda(c, argument, candidate, completionMode) analyzeLambda(c, argument, candidate, forOverloadByLambdaReturnType = false)
is LambdaWithTypeVariableAsExpectedTypeAtom -> 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) is ResolvedCallableReferenceAtom -> processCallableReference(argument, candidate)
} }
@@ -66,7 +73,7 @@ class PostponedArgumentsAnalyzer(
private fun processCallableReference(atom: ResolvedCallableReferenceAtom, candidate: Candidate) { private fun processCallableReference(atom: ResolvedCallableReferenceAtom, candidate: Candidate) {
if (atom.mightNeedAdditionalResolution) { if (atom.mightNeedAdditionalResolution) {
callResolver.resolveCallableReference(candidate.csBuilder, atom, hasSyntheticOuterCall = false) callResolver.resolveCallableReference(candidate, atom, hasSyntheticOuterCall = false)
} }
val callableReferenceAccess = atom.reference val callableReferenceAccess = atom.reference
@@ -98,17 +105,26 @@ class PostponedArgumentsAnalyzer(
c: PostponedArgumentsAnalyzerContext, c: PostponedArgumentsAnalyzerContext,
lambda: ResolvedLambdaAtom, lambda: ResolvedLambdaAtom,
candidate: Candidate, candidate: Candidate,
completionMode: ConstraintSystemCompletionMode, forOverloadByLambdaReturnType: Boolean,
//diagnosticHolder: KotlinDiagnosticsHolder //diagnosticHolder: KotlinDiagnosticsHolder
): ReturnArgumentsAnalysisResult { ): ReturnArgumentsAnalysisResult {
// TODO: replace with `require(!lambda.analyzed)` when KT-54767 will be fixed // TODO: replace with `require(!lambda.analyzed)` when KT-54767 will be fixed
if (lambda.analyzed) { if (lambda.analyzed) {
return ReturnArgumentsAnalysisResult(lambda.returnStatements, inferenceSession = null) return ReturnArgumentsAnalysisResult(lambda.returnStatements, additionalConstraints = null)
} }
val additionalBindings: Map<TypeConstructorMarker, KotlinTypeMarker>? =
(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 unitType = components.session.builtinTypes.unitType.type
val stubsForPostponedVariables = c.bindingStubsForPostponedVariables() val currentSubstitutor = c.buildCurrentSubstitutor(additionalBindings ?: emptyMap()) as ConeSubstitutor
val currentSubstitutor = c.buildCurrentSubstitutor(stubsForPostponedVariables.mapKeys { it.key.freshTypeConstructor(c) })
fun substitute(type: ConeKotlinType) = currentSubstitutor.safeSubstitute(c, type) as ConeKotlinType fun substitute(type: ConeKotlinType) = currentSubstitutor.safeSubstitute(c, type) as ConeKotlinType
@@ -126,21 +142,27 @@ class PostponedArgumentsAnalyzer(
else -> null else -> null
} }
val withPCLASession =
lambda.inputTypes
.any { inputType ->
with(c) { inputType.extractTypeVariables() }.any(c.notFixedTypeVariables::contains)
}
val results = lambdaAnalyzer.analyzeAndGetLambdaReturnArguments( val results = lambdaAnalyzer.analyzeAndGetLambdaReturnArguments(
lambda, lambda,
receiver, receiver,
contextReceivers, contextReceivers,
parameters, parameters,
expectedTypeForReturnArguments, expectedTypeForReturnArguments,
stubsForPostponedVariables, candidate,
candidate withPCLASession,
forOverloadByLambdaReturnType,
) )
applyResultsOfAnalyzedLambdaToCandidateSystem( applyResultsOfAnalyzedLambdaToCandidateSystem(
c, c,
lambda, lambda,
candidate, candidate,
results, results,
completionMode,
::substitute ::substitute
) )
return results return results
@@ -151,10 +173,13 @@ class PostponedArgumentsAnalyzer(
lambda: ResolvedLambdaAtom, lambda: ResolvedLambdaAtom,
candidate: Candidate, candidate: Candidate,
results: ReturnArgumentsAnalysisResult, 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 checkerSink: CheckerSink = CheckerSinkImpl(candidate)
val builder = c.getBuilder() 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. // If the lambda returns Unit, the last expression is not returned and should not be constrained.
val isLastExpression = it == lastExpression 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): // TODO (KT-55837) questionable moment inherited from FE1.0 (the `haveSubsystem` case):
// fun <T> foo(): T // fun <T> foo(): T
// run { // run {
@@ -181,11 +201,25 @@ class PostponedArgumentsAnalyzer(
// foo() // T = Unit, even though there is no implicit return // foo() // T = Unit, even though there is no implicit return
// } // }
// Things get even weirder if T has an upper bound incompatible with Unit. // 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) val haveSubsystem = c.addSubsystemFromExpression(it)
if (isLastExpression && !haveSubsystem && val isUnitLambda = lambdaExpectedTypeIsUnit || lambda.atom.shouldReturnUnit(returnArguments)
(lambdaExpectedTypeIsUnit || lambda.atom.shouldReturnUnit(returnArguments)) if (isLastExpression && isUnitLambda) {
) return@forEach // 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 hasExpressionInReturnArguments = true
if (!builder.hasContradiction) { if (!builder.hasContradiction) {
@@ -212,25 +246,6 @@ class PostponedArgumentsAnalyzer(
lambda.analyzed = true lambda.analyzed = true
lambda.returnStatements = returnArguments lambda.returnStatements = returnArguments
c.resolveForkPointsConstraints() 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 { fun PostponedArgumentsAnalyzerContext.createSubstituteFunctorForLambdaAnalysis(): (ConeKotlinType) -> ConeKotlinType {
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.fakeElement import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.* 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.declarations.utils.isInline
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic 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.calls.*
import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer
import org.jetbrains.kotlin.fir.resolve.diagnostics.* 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.inference.ResolvedLambdaAtom
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap 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.isWrappedIntegerOperator
import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperatorForUnsignedType import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperatorForUnsignedType
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol 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.*
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildStarProjection import org.jetbrains.kotlin.fir.types.builder.buildStarProjection
@@ -124,7 +129,7 @@ class FirCallCompletionResultsWriterTransformer(
if (mode == Mode.DelegatedPropertyCompletion) { if (mode == Mode.DelegatedPropertyCompletion) {
// Update type for `$delegateField` in `$$delegateField.get/setValue()` calls inside accessors // Update type for `$delegateField` in `$$delegateField.get/setValue()` calls inside accessors
val typeUpdater = TypeUpdaterForDelegateArguments() val typeUpdater = TypeUpdaterForPCLAAndDelegateReceivers()
qualifiedAccessExpression.transformExplicitReceiver(typeUpdater, null) qualifiedAccessExpression.transformExplicitReceiver(typeUpdater, null)
} }
@@ -137,6 +142,12 @@ class FirCallCompletionResultsWriterTransformer(
extensionReceiver = extensionReceiver?.transformSingle(integerOperatorApproximator, expectedExtensionReceiverType) extensionReceiver = extensionReceiver?.transformSingle(integerOperatorApproximator, expectedExtensionReceiverType)
} }
if (subCandidate.usedOuterCs) {
val updaterForThisReferences = TypeUpdaterForPCLAAndDelegateReceivers()
dispatchReceiver = dispatchReceiver?.transformSingle(updaterForThisReferences, null)
extensionReceiver = extensionReceiver?.transformSingle(updaterForThisReferences, null)
}
qualifiedAccessExpression.apply { qualifiedAccessExpression.apply {
replaceCalleeReference(calleeReference.toResolvedReference()) replaceCalleeReference(calleeReference.toResolvedReference())
replaceDispatchReceiver(dispatchReceiver) replaceDispatchReceiver(dispatchReceiver)
@@ -171,6 +182,21 @@ class FirCallCompletionResultsWriterTransformer(
if (declaration !is FirErrorFunction) { if (declaration !is FirErrorFunction) {
qualifiedAccessExpression.replaceTypeArguments(typeArguments) 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) session.lookupTracker?.recordTypeResolveAsLookup(type, qualifiedAccessExpression.source, context.file.source)
return qualifiedAccessExpression 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) * 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() { private fun Candidate.updateSubstitutedMemberIfReceiverContainsTypeVariable() {
val updatedSymbol = symbol.updateSubstitutedMemberIfReceiverContainsTypeVariable() ?: return val updatedSymbol = symbol.updateSubstitutedMemberIfReceiverContainsTypeVariable(usedOuterCs) ?: return
val oldSymbol = symbol val oldSymbol = symbol
@OptIn(Candidate.UpdatingSymbol::class) @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 // 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 val fir = fir
if (fir !is FirCallableDeclaration) return null if (fir !is FirCallableDeclaration) return null
@@ -240,6 +266,22 @@ class FirCallCompletionResultsWriterTransformer(
) as? FirClassSubstitutionScope ?: return null ) as? FirClassSubstitutionScope ?: return null
val original = fir.originalForSubstitutionOverride ?: 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 -> return findSingleSubstitutedSymbolWithOriginal(original.symbol) { processor ->
when (original) { when (original) {
is FirSimpleFunction -> scope.processFunctionsByName(original.name, processor) 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 { return callableReferenceAccess.apply {
replaceCalleeReference(resolvedReference) replaceCalleeReference(resolvedReference)
replaceDispatchReceiver(subCandidate.dispatchReceiverExpression()) replaceDispatchReceiver(dispatchReceiver)
replaceExtensionReceiver(subCandidate.chosenExtensionReceiverExpression()) replaceExtensionReceiver(extensionReceiver)
if (calleeReference.candidate.doesResolutionResultOverrideOtherToPreserveCompatibility()) { if (calleeReference.candidate.doesResolutionResultOverrideOtherToPreserveCompatibility()) {
addNonFatalDiagnostic(ConeResolutionResultOverridesOtherToPreserveCompatibility) addNonFatalDiagnostic(ConeResolutionResultOverridesOtherToPreserveCompatibility)
} }
@@ -533,25 +584,33 @@ class FirCallCompletionResultsWriterTransformer(
return smartCastExpression.transformOriginalExpression(this, data) return smartCastExpression.transformOriginalExpression(this, data)
} }
private inner class TypeUpdaterForDelegateArguments : FirTransformer<Any?>() { private inner class TypeUpdaterForPCLAAndDelegateReceivers : FirTransformer<Any?>() {
override fun <E : FirElement> transformElement(element: E, data: Any?): E { override fun <E : FirElement> transformElement(element: E, data: Any?): E {
return element return element
} }
override fun transformThisReceiverExpression(thisReceiverExpression: FirThisReceiverExpression, data: Any?): FirStatement {
return transformTypeRefForQualifiedAccess(thisReceiverExpression)
}
override fun transformQualifiedAccessExpression( override fun transformQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression, qualifiedAccessExpression: FirQualifiedAccessExpression,
data: Any?, data: Any?,
): FirStatement { ): 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 originalType = qualifiedAccessExpression.resolvedType
val substitutedReceiverType = finallySubstituteOrNull(originalType) ?: return qualifiedAccessExpression val substitutedReceiverType = finallySubstituteOrNull(originalType) ?: return qualifiedAccessExpression
qualifiedAccessExpression.replaceConeTypeOrNull(substitutedReceiverType) qualifiedAccessExpression.replaceConeTypeOrNull(substitutedReceiverType)
session.lookupTracker?.recordTypeResolveAsLookup(substitutedReceiverType, qualifiedAccessExpression.source, context.file.source) session.lookupTracker?.recordTypeResolveAsLookup(substitutedReceiverType, qualifiedAccessExpression.source, context.file.source)
return qualifiedAccessExpression return qualifiedAccessExpression
} }
override fun transformPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression, data: Any?): FirStatement {
return transformQualifiedAccessExpression(propertyAccessExpression, data)
}
} }
private fun FirTypeRef.substitute(candidate: Candidate): ConeKotlinType { private fun FirTypeRef.substitute(candidate: Candidate): ConeKotlinType {
@@ -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.ImplicitExtensionReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.InaccessibleImplicitReceiverValue 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.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.FirInferenceSession
import org.jetbrains.kotlin.fir.resolve.inference.FirPCLAInferenceSession
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
import org.jetbrains.kotlin.fir.resolve.transformers.withScopeCleanup import org.jetbrains.kotlin.fir.resolve.transformers.withScopeCleanup
import org.jetbrains.kotlin.fir.scopes.FirScope 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.fir.types.*
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames.UNDERSCORE_FOR_UNUSED_VAR 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 import org.jetbrains.kotlin.util.PrivateForInline
class BodyResolveContext( class BodyResolveContext(
@@ -57,6 +53,7 @@ class BodyResolveContext(
@PrivateForInline @PrivateForInline
var regularTowerDataContexts = FirRegularTowerDataContexts(regular = FirTowerDataContext()) var regularTowerDataContexts = FirRegularTowerDataContexts(regular = FirTowerDataContext())
// TODO: Rename to postponed
@PrivateForInline @PrivateForInline
val specialTowerDataContexts = FirSpecialTowerDataContexts() 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<FirFunctionSymbol<*>> = mutableSetOf() val anonymousFunctionsAnalyzedInDependentContext: MutableSet<FirFunctionSymbol<*>> = mutableSetOf()
var containingClassDeclarations: ArrayDeque<FirRegularClass> = ArrayDeque() var containingClassDeclarations: ArrayDeque<FirRegularClass> = ArrayDeque()
@@ -263,7 +250,6 @@ class BodyResolveContext(
holder.scopeSession holder.scopeSession
) )
addReceiver(labelName, receiver, additionalLabelName) addReceiver(labelName, receiver, additionalLabelName)
(inferenceSession as? FirBuilderInferenceSession)?.addLambdaImplicitReceiver(receiver)
} }
f() f()
@@ -331,11 +317,11 @@ class BodyResolveContext(
} }
@OptIn(PrivateForInline::class) @OptIn(PrivateForInline::class)
inline fun <R> withInferenceSession(inferenceSession: FirInferenceSession, block: () -> R): R { inline fun <R, S : FirInferenceSession> withInferenceSession(inferenceSession: S, block: S.() -> R): R {
val oldSession = this.inferenceSession val oldSession = this.inferenceSession
this.inferenceSession = inferenceSession this.inferenceSession = inferenceSession
return try { return try {
block() inferenceSession.block()
} finally { } finally {
this.inferenceSession = oldSession this.inferenceSession = oldSession
} }
@@ -352,10 +338,17 @@ class BodyResolveContext(
} }
@PrivateForInline @PrivateForInline
inline fun <T> withTemporaryRegularContext(newContext: FirTowerDataContext?, f: () -> T): T { inline fun <T> withTemporaryRegularContext(newContext: PostponedAtomsResolutionContext?, f: () -> T): T {
if (newContext == null) return f() val (towerDataContext, newInferenceSession) = newContext ?: return f()
return withTowerDataModeCleanup { 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. // to use information from local class inside it.
// However, we should not copy other kinds of inference sessions, // However, we should not copy other kinds of inference sessions,
// otherwise we can "inherit" type variables from there provoking inference problems // 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 inferenceSession = this@BodyResolveContext.inferenceSession
} }
} }
@@ -756,7 +749,9 @@ class BodyResolveContext(
@OptIn(PrivateForInline::class) @OptIn(PrivateForInline::class)
fun storeContextForAnonymousFunction(anonymousFunction: FirAnonymousFunction) { fun storeContextForAnonymousFunction(anonymousFunction: FirAnonymousFunction) {
specialTowerDataContexts.storeAnonymousFunctionContext(anonymousFunction.symbol, towerDataContext) specialTowerDataContexts.storeAnonymousFunctionContext(
anonymousFunction.symbol, towerDataContext, inferenceSession
)
} }
@OptIn(PrivateForInline::class) @OptIn(PrivateForInline::class)
@@ -867,38 +862,6 @@ class BodyResolveContext(
} }
} }
inline fun <T> 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 <T> withOuterConstraintStorage(
storage: ConstraintStorage,
f: () -> T
): T {
val oldStorage = this.outerConstraintStorage
this.outerConstraintStorage = storage
return try {
f()
} finally {
this.outerConstraintStorage = oldStorage
}
}
@OptIn(PrivateForInline::class) @OptIn(PrivateForInline::class)
inline fun <T> withConstructor(constructor: FirConstructor, f: () -> T): T = inline fun <T> withConstructor(constructor: FirConstructor, f: () -> T): T =
withContainer(constructor, f) withContainer(constructor, f)
@@ -956,7 +919,8 @@ class BodyResolveContext(
fun storeCallableReferenceContext(callableReferenceAccess: FirCallableReferenceAccess) { fun storeCallableReferenceContext(callableReferenceAccess: FirCallableReferenceAccess) {
specialTowerDataContexts.storeCallableReferenceContext( specialTowerDataContexts.storeCallableReferenceContext(
callableReferenceAccess, callableReferenceAccess,
towerDataContext.createSnapshot(keepMutable = false) towerDataContext.createSnapshot(keepMutable = false),
inferenceSession,
) )
} }
@@ -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.inference.extractLambdaInfoFromFunctionType
import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor 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.FirStatusResolver
import org.jetbrains.kotlin.fir.resolve.transformers.contracts.runContractResolveForFunction import org.jetbrains.kotlin.fir.resolve.transformers.contracts.runContractResolveForFunction
import org.jetbrains.kotlin.fir.resolve.transformers.transformVarargTypeToArrayType import org.jetbrains.kotlin.fir.resolve.transformers.transformVarargTypeToArrayType
@@ -329,89 +328,82 @@ open class FirDeclarationsResolveTransformer(
private fun transformPropertyAccessorsWithDelegate( private fun transformPropertyAccessorsWithDelegate(
property: FirProperty, property: FirProperty,
delegate: FirExpression, delegateContainer: FirExpression,
shouldResolveEverything: Boolean, shouldResolveEverything: Boolean,
) { ) {
val isImplicitTypedProperty = property.returnTypeRef is FirImplicitTypeRef require(delegateContainer is FirWrappedDelegateExpression)
dataFlowAnalyzer.enterDelegateExpression()
context.forPropertyDelegateAccessors(property, resolutionContext, callCompleter) { // First, resolve delegate expression in dependent context withing existing (possibly Default) inference session
dataFlowAnalyzer.enterDelegateExpression() val delegateExpression =
// Resolve delegate expression, after that, delegate will contain either expr.provideDelegate or expr // Resolve delegate expression; after that, delegate will contain either expr.provideDelegate or expr
if (property.isLocal) { if (property.isLocal) {
property.transformDelegate(transformer, ResolutionMode.ContextDependent.Delegate) transformDelegateExpression(delegateContainer)
} else { } else {
context.forPropertyInitializer { 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), // Then, create a root/child inference session based on the resolved delegate expression
// when property doesn't have explicit type. 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 // 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`. // variables from `getValue`.
// The same logic was used at K1 (see org.jetbrains.kotlin.resolve.DelegatedPropertyResolver.inferDelegateTypeFromGetSetValueMethods) // The same logic was used at K1 (see org.jetbrains.kotlin.resolve.DelegatedPropertyResolver.inferDelegateTypeFromGetSetValueMethods)
val isImplicitTypedProperty = property.returnTypeRef is FirImplicitTypeRef
property.transformAccessors( property.transformAccessors(
if (isImplicitTypedProperty) SetterResolutionMode.SKIP else SetterResolutionMode.FULLY_RESOLVE, if (isImplicitTypedProperty) SetterResolutionMode.SKIP else SetterResolutionMode.FULLY_RESOLVE,
shouldResolveEverything, shouldResolveEverything,
) )
val completedCalls = completeCandidates() completeSessionOrPostponeIfNonRoot { finalSubstitutor ->
finalSubstitutor.substituteOrNull(property.returnTypeRef.coneType)?.let { substitutedType ->
val finalSubstitutor = createFinalSubstitutor() property.replaceReturnTypeRef(property.returnTypeRef.withReplacedConeType(substitutedType))
}
finalSubstitutor.substituteOrNull(property.returnTypeRef.coneType)?.let { substitutedType -> property.getter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true)
property.replaceReturnTypeRef(property.returnTypeRef.withReplacedConeType(substitutedType)) property.setter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true)
} property.replaceReturnTypeRef(
property.getter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true) property.returnTypeRef.approximateDeclarationType(
property.setter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true) session,
property.replaceReturnTypeRef( property.visibilityForApproximation(),
property.returnTypeRef.approximateDeclarationType( property.isLocal
session, )
property.visibilityForApproximation(),
property.isLocal
) )
)
val callCompletionResultsWriter = callCompleter.createCompletionResultsWriter( // `isImplicitTypedProperty` means we haven't run setter resolution yet (see its second usage)
finalSubstitutor, if (isImplicitTypedProperty) {
mode = FirCallCompletionResultsWriterTransformer.Mode.DelegatedPropertyCompletion property.resolveSetter(mayResolveSetterBody = true, shouldResolveEverything = shouldResolveEverything)
) }
completedCalls.forEach {
it.transformSingle(callCompletionResultsWriter, null) 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 { private fun getResolvedProvideDelegateIfSuccessful(
return propertyAccessor.also { provideDelegateCall: FirFunctionCall,
transformProperty(it.propertySymbol.fir, data) resolvedDelegateExpression: FirExpression,
} ): FirFunctionCall? {
} provideDelegateCall.replaceExplicitReceiver(resolvedDelegateExpression)
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)
// Resolve call for provideDelegate, without completion // Resolve call for provideDelegate, without completion
// TODO: this generates some nodes in the control flow graph which we don't want if we // 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 // end up not selecting this option, KT-59684
transformer.expressionsTransformer?.transformFunctionCallInternal( transformer.expressionsTransformer?.transformFunctionCallInternal(
provideDelegateCall, ResolutionMode.ContextDependent, provideDelegate = true provideDelegateCall, ResolutionMode.ReceiverResolution, provideDelegate = true
) )
// If we got successful candidate for provideDelegate, let's select it // If we got successful candidate for provideDelegate, let's select it
@@ -438,10 +430,19 @@ open class FirDeclarationsResolveTransformer(
return provideDelegateCall return provideDelegateCall
} }
// Select delegate expression otherwise return null
return delegateExpression
} }
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 * For supporting the case when `provideDelegate` has a signature with type variable as a return type, like
* fun <K> K.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = this * fun <K> K.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = this
@@ -481,11 +482,6 @@ open class FirDeclarationsResolveTransformer(
val candidateSystem = candidate.system val candidateSystem = candidate.system
val candidateStorage = candidateSystem.currentStorage() 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 = val variableWithConstraints =
candidateSystem.notFixedTypeVariables[typeVariable] ?: error("Not found type variable $typeVariable") 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) // 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, // Without that, all variables (both inner and outer ones) would be considered as improper,
// while we want to fix to assume `Delegate<Tv>` as proper because `Tv` belongs to the outer system // while we want to fix to assume `Delegate<Tv>` 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) // TODO: reconsider the approach here (KT-61781 for tracking)
// Actually, this code might fail with an exception in some rare cases (see KT-61781) // 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<Y> // The problem is that in the issue example, when fixing T type variable, it has two upper bounds: X and Delegate<Y>
@@ -506,7 +502,7 @@ open class FirDeclarationsResolveTransformer(
// it seems like they do something relevant. // it seems like they do something relevant.
resultType = inferenceComponents.resultTypeResolver.findResultTypeOrNull( resultType = inferenceComponents.resultTypeResolver.findResultTypeOrNull(
candidateSystem, variableWithConstraints, TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN 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" } check(!candidateStorage.hasContradiction) { "We only should try fixing variables on successful provideDelegate candidate" }
@@ -31,7 +31,6 @@ import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.diagnostics.* 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.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.replaceLambdaArgumentInvocationKinds import org.jetbrains.kotlin.fir.resolve.transformers.replaceLambdaArgumentInvocationKinds
import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperator import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperator
@@ -91,7 +90,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
transformQualifiedAccessExpression(qualifiedAccessExpression, data, isUsedAsReceiver = false, isUsedAsGetClassReceiver = false) transformQualifiedAccessExpression(qualifiedAccessExpression, data, isUsedAsReceiver = false, isUsedAsGetClassReceiver = false)
} }
fun transformQualifiedAccessExpression( private fun transformQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression, qualifiedAccessExpression: FirQualifiedAccessExpression,
data: ResolutionMode, data: ResolutionMode,
isUsedAsReceiver: Boolean, isUsedAsReceiver: Boolean,
@@ -438,9 +437,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
functionCall functionCall
} }
val resultExpression = context.inferenceSession.onCandidatesResolution(withTransformedArguments) { val resultExpression = callResolver.resolveCallAndSelectCandidate(withTransformedArguments, data)
callResolver.resolveCallAndSelectCandidate(withTransformedArguments, data)
}
val completeInference = callCompleter.completeCall(resultExpression, data) val completeInference = callCompleter.completeCall(resultExpression, data)
val result = completeInference.transformToIntegerOperatorCallOrApproximateItIfNeeded(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) dataFlowAnalyzer.exitVariableAssignment(result)
return result return result
@@ -1053,10 +1057,10 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
val transformedLHS = when (explicitReceiver) { val transformedLHS = when (explicitReceiver) {
is FirPropertyAccessExpression -> is FirPropertyAccessExpression ->
transformQualifiedAccessExpression( transformQualifiedAccessExpression(
explicitReceiver, ResolutionMode.ContextIndependent, isUsedAsReceiver = true, isUsedAsGetClassReceiver = false explicitReceiver, ResolutionMode.ReceiverResolution.ForCallableReference, isUsedAsReceiver = true, isUsedAsGetClassReceiver = false
) as FirExpression ) as FirExpression
else -> else ->
explicitReceiver?.transformSingle(this, ResolutionMode.ContextIndependent) explicitReceiver?.transformSingle(this, ResolutionMode.ReceiverResolution.ForCallableReference)
}.apply { }.apply {
if (this is FirResolvedQualifier && callableReferenceAccess.hasQuestionMarkAtLHS) { if (this is FirResolvedQualifier && callableReferenceAccess.hasQuestionMarkAtLHS) {
replaceIsNullableLHSForCallableReference(true) replaceIsNullableLHSForCallableReference(true)
@@ -1181,7 +1185,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
constExpression.replaceKind(expressionType.toConstKind() as ConstantValueKind<T>) constExpression.replaceKind(expressionType.toConstKind() as ConstantValueKind<T>)
expressionType expressionType
} }
data is ResolutionMode.ReceiverResolution -> { data is ResolutionMode.ReceiverResolution && !data.forCallableReference -> {
require(expressionType is ConeIntegerLiteralConstantTypeImpl) require(expressionType is ConeIntegerLiteralConstantTypeImpl)
ConeIntegerConstantOperatorTypeImpl(expressionType.isUnsigned, ConeNullability.NOT_NULL) ConeIntegerConstantOperatorTypeImpl(expressionType.isUnsigned, ConeNullability.NOT_NULL)
} }
@@ -1740,4 +1744,4 @@ private fun FirFunctionCall.setArrayAugmentedAssignSource(operation: FirOperatio
this.source = newSource this.source = newSource
this.resolvedSymbol = oldCalleeReference.resolvedSymbol this.resolvedSymbol = oldCalleeReference.resolvedSymbol
}) })
} }
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.declarations.FirFunction 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.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression 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.FirConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol 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.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeVariable import org.jetbrains.kotlin.fir.types.ConeTypeVariable
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
@@ -158,6 +158,7 @@ object AdaptedCallableReferenceIsUsedWithReflection : ResolutionDiagnostic(RESOL
object TypeParameterAsExpression : ResolutionDiagnostic(INAPPLICABLE) object TypeParameterAsExpression : ResolutionDiagnostic(INAPPLICABLE)
class StubBuilderInferenceReceiver( class TypeVariableAsExplicitReceiver(
val typeParameterSymbol: FirTypeParameterSymbol val explicitReceiver: FirExpression,
) : ResolutionDiagnostic(RESOLVED_WITH_ERROR) val typeParameter: FirTypeParameter,
) : ResolutionDiagnostic(RESOLVED_WITH_ERROR)
@@ -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.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
import org.jetbrains.kotlin.types.model.* import org.jetbrains.kotlin.types.model.*
interface PostponedArgumentsAnalyzerContext : TypeSystemInferenceExtensionContext { interface PostponedArgumentsAnalyzerContext : TypeSystemInferenceExtensionContext {
fun buildCurrentSubstitutor(additionalBindings: Map<TypeConstructorMarker, StubTypeMarker>): TypeSubstitutorMarker val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>
fun buildCurrentSubstitutor(additionalBindings: Map<TypeConstructorMarker, KotlinTypeMarker>): TypeSubstitutorMarker
fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker
fun bindingStubsForPostponedVariables(): Map<TypeVariableMarker, StubTypeMarker> fun bindingStubsForPostponedVariables(): Map<TypeVariableMarker, StubTypeMarker>
@@ -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.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker 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.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeConstructorMarker import org.jetbrains.kotlin.types.model.TypeConstructorMarker
import org.jetbrains.kotlin.types.model.TypeVariableMarker import org.jetbrains.kotlin.types.model.TypeVariableMarker
abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Context, ResultTypeResolver.Context { abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Context, ResultTypeResolver.Context {
abstract val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker>
abstract override val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints> abstract override val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>
abstract override val fixedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker> abstract override val fixedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker>
abstract override val postponedTypeVariables: List<TypeVariableMarker> abstract override val postponedTypeVariables: List<TypeVariableMarker>
/**
* See [org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.outerSystemVariablesPrefixSize]
*/
abstract val outerSystemVariablesPrefixSize: Int
abstract fun getBuilder(): ConstraintSystemBuilder abstract fun getBuilder(): ConstraintSystemBuilder
// type can be proper if it not contains not fixed type variables // type can be proper if it not contains not fixed type variables
@@ -69,7 +64,7 @@ abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Contex
completionMode: ConstraintSystemCompletionMode, completionMode: ConstraintSystemCompletionMode,
analyze: (A) -> Unit analyze: (A) -> Unit
): Boolean { ): Boolean {
if (completionMode == ConstraintSystemCompletionMode.FULL) { if (completionMode.allLambdasShouldBeAnalyzed) {
val argumentWithTypeVariableAsExpectedType = findPostponedArgumentWithRevisableExpectedType(postponedArguments) val argumentWithTypeVariableAsExpectedType = findPostponedArgumentWithRevisableExpectedType(postponedArguments)
if (argumentWithTypeVariableAsExpectedType != null) { if (argumentWithTypeVariableAsExpectedType != null) {
@@ -120,4 +115,11 @@ abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Contex
!it.typeConstructor().isClassTypeConstructor() && !it.typeConstructor().isTypeParameterTypeConstructor() !it.typeConstructor().isClassTypeConstructor() && !it.typeConstructor().isTypeParameterTypeConstructor()
} }
}.map { it.type } }.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 <R> withTypeVariablesThatAreCountedAsProperTypes(typeVariables: Set<TypeConstructorMarker>, block: () -> R): R
} }
@@ -5,8 +5,9 @@
package org.jetbrains.kotlin.resolve.calls.inference.components package org.jetbrains.kotlin.resolve.calls.inference.components
enum class ConstraintSystemCompletionMode { enum class ConstraintSystemCompletionMode(val allLambdasShouldBeAnalyzed: Boolean) {
FULL, 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 * 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 * x.plus(run { x }) // Here, to select plus overload we need to analyze lambda
* ``` * ```
*/ */
PARTIAL, PARTIAL(false),
UNTIL_FIRST_LAMBDA UNTIL_FIRST_LAMBDA(false),
} }
@@ -9,8 +9,13 @@ import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind
import org.jetbrains.kotlin.builtins.functions.isBasicFunctionOrKFunction import org.jetbrains.kotlin.builtins.functions.isBasicFunctionOrKFunction
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
import org.jetbrains.kotlin.resolve.calls.model.* 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.types.model.*
import org.jetbrains.kotlin.utils.SmartSet import org.jetbrains.kotlin.utils.SmartSet
import java.util.* import java.util.*
@@ -531,24 +536,40 @@ class PostponedArgumentInputTypesResolver(
postponedArguments: List<PostponedResolvedAtomMarker>, postponedArguments: List<PostponedResolvedAtomMarker>,
topLevelType: KotlinTypeMarker, topLevelType: KotlinTypeMarker,
dependencyProvider: TypeVariableDependencyInformationProvider, dependencyProvider: TypeVariableDependencyInformationProvider,
resolvedAtomProvider: ResolvedAtomProvider resolvedAtomProvider: ResolvedAtomProvider,
): Boolean = with(c) { ): Boolean {
val expectedType = argument.run { (this as? PostponedAtomWithRevisableExpectedType)?.revisedExpectedType ?: expectedType } val expectedType = argument.expectedFunctionType(c) ?: return false
if (expectedType != null && expectedType.isFunctionOrKFunctionWithAnySuspendability()) { return c.fixNextReadyVariableForParameterType(
val wasFixedSomeVariable = c.fixNextReadyVariableForParameterType( expectedType,
expectedType, postponedArguments,
postponedArguments, topLevelType,
topLevelType, dependencyProvider,
dependencyProvider, resolvedAtomProvider,
resolvedAtomProvider )
) }
if (wasFixedSomeVariable) @K2Only
return true fun findNextVariableForReportingNotInferredInputType(
} c: Context,
argument: PostponedResolvedAtomMarker,
postponedArguments: List<PostponedResolvedAtomMarker>,
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( private fun Context.fixNextReadyVariableForParameterType(
@@ -558,17 +579,9 @@ class PostponedArgumentInputTypesResolver(
dependencyProvider: TypeVariableDependencyInformationProvider, dependencyProvider: TypeVariableDependencyInformationProvider,
resolvedAtomByTypeVariableProvider: ResolvedAtomProvider, resolvedAtomByTypeVariableProvider: ResolvedAtomProvider,
): Boolean = with(resolutionTypeSystemContext) { ): Boolean = with(resolutionTypeSystemContext) {
val relatedVariables = type.extractArgumentsForFunctionTypeOrSubtype() val variableForFixation = findNextVariableForParameterType(type, dependencyProvider, postponedArguments, topLevelType)
.flatMap { getAllDeeplyRelatedTypeVariables(it, dependencyProvider) }
val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
this@fixNextReadyVariableForParameterType,
relatedVariables,
postponedArguments,
ConstraintSystemCompletionMode.FULL,
topLevelType
)
if (variableForFixation == null || !variableForFixation.hasProperConstraint) if (variableForFixation == null || !variableForFixation.isReady)
return false return false
val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable)
@@ -589,6 +602,26 @@ class PostponedArgumentInputTypesResolver(
return true return true
} }
private fun Context.findNextVariableForParameterType(
type: KotlinTypeMarker,
dependencyProvider: TypeVariableDependencyInformationProvider,
postponedArguments: List<PostponedResolvedAtomMarker>,
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) } private fun KotlinTypeMarker?.wrapToTypeWithKind() = this?.let { TypeWithKind(it) }
companion object { companion object {
@@ -22,6 +22,8 @@ class ResultTypeResolver(
private val languageVersionSettings: LanguageVersionSettings private val languageVersionSettings: LanguageVersionSettings
) { ) {
interface Context : TypeSystemInferenceExtensionContext { interface Context : TypeSystemInferenceExtensionContext {
val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>
val outerSystemVariablesPrefixSize: Int
fun isProperType(type: KotlinTypeMarker): Boolean fun isProperType(type: KotlinTypeMarker): Boolean
fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker
fun isReified(variable: TypeVariableMarker): Boolean fun isReified(variable: TypeVariableMarker): Boolean
@@ -220,6 +222,10 @@ class ResultTypeResolver(
if (typesWithoutStubs.isNotEmpty()) { if (typesWithoutStubs.isNotEmpty()) {
commonSuperType = computeCommonSuperType(typesWithoutStubs) 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() 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 if (!atLeastOneNonProper) return lowerConstraintTypes
val notFixedToStubTypesSubstitutor = buildNotFixedVariablesToStubTypesSubstitutor() val notFixedToStubTypesSubstitutor = buildNotFixedVariablesToStubTypesSubstitutor()
@@ -336,7 +350,7 @@ class ResultTypeResolver(
} }
private fun Context.isProperTypeForFixation(type: KotlinTypeMarker): Boolean = 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? = private fun findResultIfThereIsEqualsConstraint(c: Context, variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? =
with(c) { with(c) {
@@ -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.inference.model.VariableWithConstraints
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker 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 import org.jetbrains.kotlin.utils.SmartSet
class TypeVariableDependencyInformationProvider( class TypeVariableDependencyInformationProvider(
private val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>, private val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>,
private val postponedKtPrimitives: List<PostponedResolvedAtomMarker>, private val postponedKtPrimitives: List<PostponedResolvedAtomMarker>,
private val topLevelType: KotlinTypeMarker?, private val topLevelType: KotlinTypeMarker?,
private val typeSystemContext: TypeSystemInferenceExtensionContext private val typeSystemContext: VariableFixationFinder.Context
) { ) {
private val outerTypeVariables: Set<TypeConstructorMarker>? =
typeSystemContext.outerTypeVariables
/* /*
* Not oriented edges * Not oriented edges
* TypeVariable(A) has UPPER(Function1<TypeVariable(B), R>) => A and B are related deeply * TypeVariable(A) has UPPER(Function1<TypeVariable(B), R>) => A and B are related deeply
@@ -41,7 +48,16 @@ class TypeVariableDependencyInformationProvider(
computeRelatedToTopLevelType() 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 isVariableRelatedToAnyOutputType(variable: TypeConstructorMarker) = relatedToAllOutputTypes.contains(variable)
fun getDeeplyDependentVariables(variable: TypeConstructorMarker) = deepTypeVariableDependencies[variable] fun getDeeplyDependentVariables(variable: TypeConstructorMarker) = deepTypeVariableDependencies[variable]
@@ -27,25 +27,40 @@ class VariableFixationFinder(
val fixedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker> val fixedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker>
val postponedTypeVariables: List<TypeVariableMarker> val postponedTypeVariables: List<TypeVariableMarker>
val constraintsFromAllForkPoints: MutableList<Pair<IncorporationConstraintPosition, ForkPointData>> val constraintsFromAllForkPoints: MutableList<Pair<IncorporationConstraintPosition, ForkPointData>>
val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker>
/** /**
* If not null, that property means that we should assume temporary * See [org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.outerSystemVariablesPrefixSize]
* `allTypeVariables.keys.minus(typeVariablesThatAreNotCountedAsProperTypes)` as proper types when fixating some variables. */
val outerSystemVariablesPrefixSize: Int
val outerTypeVariables: Set<TypeConstructorMarker>?
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. * By default, if that property is null, we assume all `allTypeVariables` as not proper.
* *
* Currently, that is only used for `provideDelegate` resolution, see * Currently, that is only used for `provideDelegate` resolution, see
* [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded] * [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded]
*/ */
val typeVariablesThatAreNotCountedAsProperTypes: Set<TypeConstructorMarker>? val typeVariablesThatAreCountedAsProperTypes: Set<TypeConstructorMarker>?
fun isReified(variable: TypeVariableMarker): Boolean fun isReified(variable: TypeVariableMarker): Boolean
} }
data class VariableForFixation( class VariableForFixation(
val variable: TypeConstructorMarker, val variable: TypeConstructorMarker,
val hasProperConstraint: Boolean, private val hasProperConstraint: Boolean,
) private val hasDependencyOnOuterTypeVariable: Boolean = false,
) {
val isReady: Boolean get() = hasProperConstraint && !hasDependencyOnOuterTypeVariable
}
fun findFirstVariableForFixation( fun findFirstVariableForFixation(
c: Context, c: Context,
@@ -53,11 +68,13 @@ class VariableFixationFinder(
postponedKtPrimitives: List<PostponedResolvedAtomMarker>, postponedKtPrimitives: List<PostponedResolvedAtomMarker>,
completionMode: ConstraintSystemCompletionMode, completionMode: ConstraintSystemCompletionMode,
topLevelType: KotlinTypeMarker, topLevelType: KotlinTypeMarker,
): VariableForFixation? = c.findTypeVariableForFixation(allTypeVariables, postponedKtPrimitives, completionMode, topLevelType) ): VariableForFixation? =
c.findTypeVariableForFixation(allTypeVariables, postponedKtPrimitives, completionMode, topLevelType)
enum class TypeVariableFixationReadiness { enum class TypeVariableFixationReadiness {
FORBIDDEN, FORBIDDEN,
WITHOUT_PROPER_ARGUMENT_CONSTRAINT, // proper constraint from arguments -- not from upper bound for type parameters 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, 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<S> WITH_COMPLEX_DEPENDENCY, // if type variable T has constraint with non fixed type variable inside (non-top-level): T <: Foo<S>
ALL_CONSTRAINTS_TRIVIAL_OR_NON_PROPER, // proper trivial constraint from arguments, Nothing <: T ALL_CONSTRAINTS_TRIVIAL_OR_NON_PROPER, // proper trivial constraint from arguments, Nothing <: T
@@ -85,6 +102,7 @@ class VariableFixationFinder(
isTypeInferenceForSelfTypesSupported && areAllProperConstraintsSelfTypeBased(variable) -> isTypeInferenceForSelfTypesSupported && areAllProperConstraintsSelfTypeBased(variable) ->
TypeVariableFixationReadiness.READY_FOR_FIXATION_DECLARED_UPPER_BOUND_WITH_SELF_TYPES TypeVariableFixationReadiness.READY_FOR_FIXATION_DECLARED_UPPER_BOUND_WITH_SELF_TYPES
!variableHasProperArgumentConstraints(variable) -> TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT !variableHasProperArgumentConstraints(variable) -> TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT
dependencyProvider.isRelatedToOuterTypeVariable(variable) -> TypeVariableFixationReadiness.OUTER_TYPE_VARIABLE_DEPENDENCY
hasDependencyToOtherTypeVariables(variable) -> TypeVariableFixationReadiness.WITH_COMPLEX_DEPENDENCY hasDependencyToOtherTypeVariables(variable) -> TypeVariableFixationReadiness.WITH_COMPLEX_DEPENDENCY
// TODO: Consider removing this kind of readiness, see KT-63032 // TODO: Consider removing this kind of readiness, see KT-63032
allConstraintsTrivialOrNonProper(variable) -> TypeVariableFixationReadiness.ALL_CONSTRAINTS_TRIVIAL_OR_NON_PROPER allConstraintsTrivialOrNonProper(variable) -> TypeVariableFixationReadiness.ALL_CONSTRAINTS_TRIVIAL_OR_NON_PROPER
@@ -152,7 +170,7 @@ class VariableFixationFinder(
if (allTypeVariables.isEmpty()) return null if (allTypeVariables.isEmpty()) return null
val dependencyProvider = TypeVariableDependencyInformationProvider( val dependencyProvider = TypeVariableDependencyInformationProvider(
notFixedTypeVariables, postponedArguments, topLevelType.takeIf { completionMode == PARTIAL }, this notFixedTypeVariables, postponedArguments, topLevelType.takeIf { completionMode == PARTIAL }, this,
) )
val candidate = val candidate =
@@ -161,6 +179,8 @@ class VariableFixationFinder(
return when (getTypeVariableReadiness(candidate, dependencyProvider)) { return when (getTypeVariableReadiness(candidate, dependencyProvider)) {
TypeVariableFixationReadiness.FORBIDDEN -> null TypeVariableFixationReadiness.FORBIDDEN -> null
TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT -> VariableForFixation(candidate, false) TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT -> VariableForFixation(candidate, false)
TypeVariableFixationReadiness.OUTER_TYPE_VARIABLE_DEPENDENCY ->
VariableForFixation(candidate, hasProperConstraint = true, hasDependencyOnOuterTypeVariable = true)
else -> VariableForFixation(candidate, true) else -> VariableForFixation(candidate, true)
} }
@@ -190,12 +210,13 @@ class VariableFixationFinder(
&& !c.isNullabilityConstraint && !c.isNullabilityConstraint
private fun Context.isProperType(type: KotlinTypeMarker): Boolean = 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 { private fun Context.isNotFixedRelevantVariable(it: KotlinTypeMarker): Boolean {
if (!notFixedTypeVariables.containsKey(it.typeConstructor())) return false val key = it.typeConstructor()
if (typeVariablesThatAreNotCountedAsProperTypes == null) return true if (!notFixedTypeVariables.containsKey(key)) return false
return typeVariablesThatAreNotCountedAsProperTypes!!.contains(it.typeConstructor()) if (typeVariablesThatAreCountedAsProperTypes?.contains(key) == true) return false
return true
} }
private fun Context.isReified(variable: TypeConstructorMarker): Boolean = 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<TypeConstructorMarker>,
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<F>
if (type.typeConstructor() in notFixedTypeVariables) return false
return isProper(type) && extractProjectionsForAllCapturedTypes(type).all(isProper)
}
fun TypeSystemInferenceExtensionContext.extractProjectionsForAllCapturedTypes(baseType: KotlinTypeMarker): Set<KotlinTypeMarker> { fun TypeSystemInferenceExtensionContext.extractProjectionsForAllCapturedTypes(baseType: KotlinTypeMarker): Set<KotlinTypeMarker> {
if (baseType.isFlexible()) { if (baseType.isFlexible()) {
@@ -49,6 +49,8 @@ interface ConstraintStorage {
val constraintsFromAllForkPoints: List<Pair<IncorporationConstraintPosition, ForkPointData>> val constraintsFromAllForkPoints: List<Pair<IncorporationConstraintPosition, ForkPointData>>
/** /**
* 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 * 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. * of [allTypeVariables] belong to the outer CS.
* *
@@ -62,6 +64,8 @@ interface ConstraintStorage {
*/ */
val outerSystemVariablesPrefixSize: Int val outerSystemVariablesPrefixSize: Int
val usesOuterCs: Boolean
object Empty : ConstraintStorage { object Empty : ConstraintStorage {
override val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker> get() = emptyMap() override val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker> get() = emptyMap()
override val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints> get() = emptyMap() override val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints> get() = emptyMap()
@@ -77,6 +81,8 @@ interface ConstraintStorage {
override val constraintsFromAllForkPoints: List<Pair<IncorporationConstraintPosition, ForkPointData>> = emptyList() override val constraintsFromAllForkPoints: List<Pair<IncorporationConstraintPosition, ForkPointData>> = emptyList()
override val outerSystemVariablesPrefixSize: Int get() = 0 override val outerSystemVariablesPrefixSize: Int get() = 0
override val usesOuterCs: Boolean get() = false
} }
} }
@@ -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<Int, List<Constraint>>): Boolean { private fun isUsefulConstraint(constraint: Constraint, equalityConstraints: Map<Int, List<Constraint>>): Boolean {
if (constraint.kind == ConstraintKind.EQUALITY) return true if (constraint.kind == ConstraintKind.EQUALITY) return true
return equalityConstraints[constraint.typeHashCode]?.none { it.type == constraint.type } ?: true return equalityConstraints[constraint.typeHashCode]?.none { it.type == constraint.type } ?: true
@@ -251,4 +259,15 @@ internal class MutableConstraintStorage : ConstraintStorage {
override val constraintsFromAllForkPoints: MutableList<Pair<IncorporationConstraintPosition, ForkPointData>> = SmartList() override val constraintsFromAllForkPoints: MutableList<Pair<IncorporationConstraintPosition, ForkPointData>> = SmartList()
override var outerSystemVariablesPrefixSize: Int = 0 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
@@ -41,7 +41,7 @@ class NewConstraintSystemImpl(
private val properTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create() private val properTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
private val notProperTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create() private val notProperTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
private val intersectionTypesCache: MutableMap<Collection<KotlinTypeMarker>, EmptyIntersectionTypeInfo?> = mutableMapOf() private val intersectionTypesCache: MutableMap<Collection<KotlinTypeMarker>, EmptyIntersectionTypeInfo?> = mutableMapOf()
override var typeVariablesThatAreNotCountedAsProperTypes: Set<TypeConstructorMarker>? = null override var typeVariablesThatAreCountedAsProperTypes: Set<TypeConstructorMarker>? = null
private var couldBeResolvedWithUnrestrictedBuilderInference: Boolean = false 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.resolve.calls.inference.components.VariableFixationFinder.Context.typeVariablesThatAreNotCountedAsProperTypes]
* @see [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded] * @see [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded]
*/ */
fun withTypeVariablesThatAreNotCountedAsProperTypes(typeVariables: Set<TypeConstructorMarker>, block: () -> Unit) { @K2Only
override fun <R> withTypeVariablesThatAreCountedAsProperTypes(typeVariables: Set<TypeConstructorMarker>, block: () -> R): R {
checkState(State.BUILDING) checkState(State.BUILDING)
// Cleaning cache is necessary because temporarily we change the meaning of what does "proper type" mean // Cleaning cache is necessary because temporarily we change the meaning of what does "proper type" mean
properTypesCache.clear() properTypesCache.clear()
notProperTypesCache.clear() notProperTypesCache.clear()
require(typeVariablesThatAreNotCountedAsProperTypes == null) { require(typeVariablesThatAreCountedAsProperTypes == null) {
"Currently there should be no nested withDisallowingOnlyThisTypeVariablesForProperTypes calls" "Currently there should be no nested withDisallowingOnlyThisTypeVariablesForProperTypes calls"
} }
typeVariablesThatAreNotCountedAsProperTypes = typeVariables typeVariablesThatAreCountedAsProperTypes = typeVariables
block() val result = block()
typeVariablesThatAreNotCountedAsProperTypes = null typeVariablesThatAreCountedAsProperTypes = null
properTypesCache.clear() properTypesCache.clear()
notProperTypesCache.clear() notProperTypesCache.clear()
return result
} }
private enum class State { private enum class State {
@@ -221,6 +224,7 @@ class NewConstraintSystemImpl(
// ConstraintSystemBuilder // ConstraintSystemBuilder
private fun transactionRegisterVariable(variable: TypeVariableMarker) { private fun transactionRegisterVariable(variable: TypeVariableMarker) {
if (state != State.TRANSACTION) return if (state != State.TRANSACTION) return
if (variable.freshTypeConstructor() in storage.allTypeVariables) return
typeVariablesTransaction.add(variable) typeVariablesTransaction.add(variable)
} }
@@ -302,13 +306,25 @@ class NewConstraintSystemImpl(
} }
fun addOuterSystem(outerSystem: ConstraintStorage) { fun addOuterSystem(outerSystem: ConstraintStorage) {
addOtherSystem(outerSystem) require(!storage.usesOuterCs)
storage.usesOuterCs = true
storage.outerSystemVariablesPrefixSize = outerSystem.allTypeVariables.size storage.outerSystemVariablesPrefixSize = outerSystem.allTypeVariables.size
@OptIn(AssertionsOnly::class)
storage.outerCS = outerSystem
addOtherSystem(outerSystem, isAddingOuter = true)
} }
fun setBaseSystem(outerSystem: ConstraintStorage) { @K2Only
addOtherSystem(outerSystem) fun setBaseSystem(baseSystem: ConstraintStorage) {
storage.outerSystemVariablesPrefixSize = outerSystem.outerSystemVariablesPrefixSize 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() { fun prepareForGlobalCompletion() {
@@ -317,6 +333,17 @@ class NewConstraintSystemImpl(
} }
override fun addOtherSystem(otherSystem: ConstraintStorage) { 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()) { if (otherSystem.allTypeVariables.isNotEmpty()) {
otherSystem.allTypeVariables.forEach { otherSystem.allTypeVariables.forEach {
transactionRegisterVariable(it.value) transactionRegisterVariable(it.value)
@@ -324,16 +351,47 @@ class NewConstraintSystemImpl(
storage.allTypeVariables.putAll(otherSystem.allTypeVariables) storage.allTypeVariables.putAll(otherSystem.allTypeVariables)
notProperTypesCache.clear() 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) { for ((variable, constraints) in otherSystem.notFixedTypeVariables) {
notFixedTypeVariables[variable] = MutableVariableWithConstraints(this, constraints) 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 = storage.maxTypeDepthFromInitialConstraints =
max(storage.maxTypeDepthFromInitialConstraints, otherSystem.maxTypeDepthFromInitialConstraints) max(storage.maxTypeDepthFromInitialConstraints, otherSystem.maxTypeDepthFromInitialConstraints)
storage.errors.addAll(otherSystem.errors) storage.errors.addAll(otherSystem.errors)
storage.fixedTypeVariables.putAll(otherSystem.fixedTypeVariables) storage.fixedTypeVariables.putAll(otherSystem.fixedTypeVariables)
storage.postponedTypeVariables.addAll(otherSystem.postponedTypeVariables) storage.postponedTypeVariables.addAll(otherSystem.postponedTypeVariables)
storage.constraintsFromAllForkPoints.addAll(otherSystem.constraintsFromAllForkPoints) 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 // ResultTypeResolver.Context, ConstraintSystemBuilder
@@ -357,11 +415,11 @@ class NewConstraintSystemImpl(
it it
if (typeToCheck == null) return@contains false if (typeToCheck == null) return@contains false
if (typeVariablesThatAreNotCountedAsProperTypes != null) { if (typeVariablesThatAreCountedAsProperTypes?.contains(typeToCheck.typeConstructor()) == true) {
return@contains typeVariablesThatAreNotCountedAsProperTypes!!.contains(typeToCheck.typeConstructor()) return@contains false
} }
storage.allTypeVariables.containsKey(typeToCheck.typeConstructor()) return@contains storage.allTypeVariables.containsKey(typeToCheck.typeConstructor())
} }
override fun isTypeVariable(type: KotlinTypeMarker): Boolean { override fun isTypeVariable(type: KotlinTypeMarker): Boolean {
@@ -689,7 +747,7 @@ class NewConstraintSystemImpl(
return buildCurrentSubstitutor(emptyMap()) return buildCurrentSubstitutor(emptyMap())
} }
override fun buildCurrentSubstitutor(additionalBindings: Map<TypeConstructorMarker, StubTypeMarker>): TypeSubstitutorMarker { override fun buildCurrentSubstitutor(additionalBindings: Map<TypeConstructorMarker, KotlinTypeMarker>): TypeSubstitutorMarker {
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
return storage.buildCurrentSubstitutor(this, additionalBindings) return storage.buildCurrentSubstitutor(this, additionalBindings)
} }
@@ -715,6 +773,8 @@ class NewConstraintSystemImpl(
return storage return storage
} }
val usesOuterCs: Boolean get() = storage.usesOuterCs
// PostponedArgumentsAnalyzer.Context // PostponedArgumentsAnalyzer.Context
override fun hasUpperOrEqualUnitConstraint(type: KotlinTypeMarker): Boolean { override fun hasUpperOrEqualUnitConstraint(type: KotlinTypeMarker): Boolean {
checkState(State.BUILDING, State.COMPLETION, State.FREEZED) checkState(State.BUILDING, State.COMPLETION, State.FREEZED)
@@ -10,22 +10,25 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor 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.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.components.candidate.SimpleResolutionCandidate
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.addEqualityConstraintIfCompatible 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.ConstraintStorage.Empty.hasContradiction
import org.jetbrains.kotlin.resolve.calls.inference.model.ExpectedTypeConstraintPositionImpl import org.jetbrains.kotlin.resolve.calls.inference.model.ExpectedTypeConstraintPositionImpl
import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.tower.CandidateFactory import org.jetbrains.kotlin.resolve.calls.tower.CandidateFactory
import org.jetbrains.kotlin.resolve.calls.tower.forceResolution 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.TypeUtils
import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.error.ErrorType import org.jetbrains.kotlin.types.error.ErrorType
import org.jetbrains.kotlin.types.error.ErrorTypeKind 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.types.model.safeSubstitute
import org.jetbrains.kotlin.utils.addToStdlib.same import org.jetbrains.kotlin.utils.addToStdlib.same
@@ -80,6 +83,7 @@ class KotlinCallCompleter(
candidate.runCompletion(completionMode, diagnosticHolder, resolutionCallbacks) candidate.runCompletion(completionMode, diagnosticHolder, resolutionCallbacks)
candidate.asCallResolutionResult(completionMode, diagnosticHolder) 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") ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA -> throw IllegalStateException("Should not be here")
} }
@@ -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.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.model.MultiLambdaBuilderInferenceRestriction 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.ErrorTypeKind
import org.jetbrains.kotlin.types.error.ErrorUtils import org.jetbrains.kotlin.types.error.ErrorUtils
import org.jetbrains.kotlin.types.model.* 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 // continue completion (rerun stages) only if ready for fixation variables with proper constraints have appeared
// (after analysing a lambda with the builder inference) // (after analysing a lambda with the builder inference)
// otherwise we don't continue and report "not enough type information" error // otherwise we don't continue and report "not enough type information" error
return variableForFixation?.hasProperConstraint == true return variableForFixation?.isReady == true
} }
private fun transformToAtomWithNewFunctionalExpectedType( private fun transformToAtomWithNewFunctionalExpectedType(
@@ -319,7 +322,7 @@ class KotlinConstraintSystemCompleter(
topLevelType topLevelType
) ?: return false ) ?: return false
if (!variableForFixation.hasProperConstraint) return false if (!variableForFixation.isReady) return false
fixVariable(this, notFixedTypeVariables.getValue(variableForFixation.variable), topLevelAtoms, diagnosticsHolder) fixVariable(this, notFixedTypeVariables.getValue(variableForFixation.variable), topLevelAtoms, diagnosticsHolder)
@@ -340,7 +343,7 @@ class KotlinConstraintSystemCompleter(
postponedArguments, completionMode, topLevelType, postponedArguments, completionMode, topLevelType,
) ?: break ) ?: break
assert(!variableForFixation.hasProperConstraint) { assert(!variableForFixation.isReady) {
"At this stage there should be no remaining variables with proper constraints" "At this stage there should be no remaining variables with proper constraints"
} }
@@ -8,7 +8,9 @@ package org.jetbrains.kotlin.types.model
import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind
import org.jetbrains.kotlin.resolve.checkers.EmptyIntersectionTypeChecker import org.jetbrains.kotlin.resolve.checkers.EmptyIntersectionTypeChecker
import org.jetbrains.kotlin.resolve.checkers.EmptyIntersectionTypeInfo 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.ExperimentalContracts
import kotlin.contracts.contract import kotlin.contracts.contract
@@ -310,6 +312,9 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui
*/ */
fun KotlinTypeMarker.convertToNonRaw(): KotlinTypeMarker fun KotlinTypeMarker.convertToNonRaw(): KotlinTypeMarker
@K2Only
fun createSubstitutionFromSubtypingStubTypesToTypeVariables(): TypeSubstitutorMarker
fun createCapturedStarProjectionForSelfType( fun createCapturedStarProjectionForSelfType(
typeVariable: TypeVariableTypeConstructorMarker, typeVariable: TypeVariableTypeConstructorMarker,
typesForRecursiveTypeParameters: List<KotlinTypeMarker>, typesForRecursiveTypeParameters: List<KotlinTypeMarker>,
@@ -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") @RequiresOptIn("This kinds of type is obsolete and should not be used until you really need it")
annotation class ObsoleteTypeKind annotation class ObsoleteTypeKind
@RequiresOptIn
annotation class K2Only
@@ -622,6 +622,11 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy
errorSupportedOnlyInTypeInference() errorSupportedOnlyInTypeInference()
} }
@K2Only
override fun createSubstitutionFromSubtypingStubTypesToTypeVariables(): TypeSubstitutorMarker {
error("Only for K2")
}
override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker { override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker {
require(type is UnwrappedType, type::errorMessage) require(type is UnwrappedType, type::errorMessage)
require(this is TypeSubstitutor, this::errorMessage) require(this is TypeSubstitutor, this::errorMessage)