~ WIP
diff --git a/.idea/dictionaries/Denis_Zharkov.xml b/.idea/dictionaries/Denis_Zharkov.xml
new file mode 100644
index 0000000..3251fd1
--- /dev/null
+++ b/.idea/dictionaries/Denis_Zharkov.xml
@@ -0,0 +1,7 @@
+<component name="ProjectDictionaryState">
+ <dictionary name="Denis.Zharkov">
+ <words>
+ <w>pcla</w>
+ </words>
+ </dictionary>
+</component>
\ No newline at end of file
diff --git a/compiler/fir/build.gradle.kts b/compiler/fir/build.gradle.kts
index aff8f2e..af70e2c 100644
--- a/compiler/fir/build.gradle.kts
+++ b/compiler/fir/build.gradle.kts
@@ -22,6 +22,7 @@
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>().configureEach {
kotlinOptions {
freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.fir.symbols.SymbolInternals"
+ freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.types.model.K2Only"
}
}
}
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt
index 69fa4dd..9636df5 100644
--- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt
@@ -16,7 +16,6 @@
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember
import org.jetbrains.kotlin.fir.analysis.getChild
import org.jetbrains.kotlin.fir.builder.FirSyntaxErrors
-import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.diagnostics.*
@@ -317,13 +316,13 @@
rootCause.expectedContextReceiverType.removeTypeVariableTypes(typeContext)
)
- is StubBuilderInferenceReceiver -> {
- val typeParameterSymbol = rootCause.typeParameterSymbol
+ is TypeVariableAsExplicitReceiver -> {
+ val typeParameter = rootCause.typeParameter
@OptIn(SymbolInternals::class)
FirErrors.BUILDER_INFERENCE_STUB_RECEIVER.createOn(
- qualifiedAccessSource ?: source,
- typeParameterSymbol.name,
- (typeParameterSymbol.containingDeclarationSymbol.fir as FirMemberDeclaration).nameOrSpecialName
+ rootCause.explicitReceiver.source,
+ typeParameter.symbol.name,
+ (typeParameter.symbol.containingDeclarationSymbol.fir as FirMemberDeclaration).nameOrSpecialName
)
}
diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeVariableAndStubTypes.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeVariableAndStubTypes.kt
index 3444ca8..85eae6a 100644
--- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeVariableAndStubTypes.kt
+++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeVariableAndStubTypes.kt
@@ -46,6 +46,8 @@
fun recordInfoAboutTypeVariableUsagesAsInvariantOrContravariantParameter() {
isContainedInInvariantOrContravariantPositions = true
}
+
+ override fun toString(): String = "${this::class.simpleName}($debugName)"
}
// ----------------------------------- Stub types -----------------------------------
diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt
index 9878b0f..f0d695d 100644
--- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt
+++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt
@@ -125,6 +125,13 @@
is ConeDefinitelyNotNullType -> original.scope(useSiteSession, scopeSession, requiredMembersPhase)
is ConeIntegerConstantOperatorType -> scopeSession.getOrBuildScopeForIntegerConstantOperatorType(useSiteSession, this)
is ConeIntegerLiteralConstantType -> error("ILT should not be in receiver position")
+ // See testData/diagnostics/tests/inference/builderInference/memberScopeOfCapturedTypeForPostponedCall.kt
+ is ConeCapturedType -> {
+ val supertypes =
+ constructor.supertypes?.takeIf { it.isNotEmpty() }
+ ?: listOf(useSiteSession.builtinTypes.anyType.type)
+ useSiteSession.typeContext.intersectTypes(supertypes).scope(useSiteSession, scopeSession, requiredMembersPhase)
+ }
else -> null
}
diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt
index d0290e8..9c6b447 100644
--- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt
+++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt
@@ -9,7 +9,6 @@
import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
-import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic
import org.jetbrains.kotlin.fir.expressions.FirCheckNotNullCall
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression
@@ -18,7 +17,6 @@
import org.jetbrains.kotlin.fir.expressions.builder.buildSmartCastExpression
import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpression
import org.jetbrains.kotlin.fir.references.builder.buildImplicitThisReference
-import org.jetbrains.kotlin.fir.renderWithType
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.scope
import org.jetbrains.kotlin.fir.resolve.smartcastScope
@@ -28,8 +26,10 @@
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirScriptSymbol
-import org.jetbrains.kotlin.fir.types.*
+import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
+import org.jetbrains.kotlin.fir.types.constructType
+import org.jetbrains.kotlin.fir.types.resolvedType
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.SmartcastStability
@@ -89,6 +89,7 @@
abstract val isContextReceiver: Boolean
+ // Type before smart cast
val originalType: ConeKotlinType = type
var implicitScope: FirTypeScope? =
@@ -145,19 +146,6 @@
@RequiresOptIn
annotation class ImplicitReceiverInternals
- @Deprecated(level = DeprecationLevel.ERROR, message = "Builder inference should not modify implicit receivers. KT-54708")
- fun updateTypeInBuilderInference(type: ConeKotlinType) {
- this.type = type
- originalReceiverExpression = receiverExpression(boundSymbol, type, contextReceiverNumber, inaccessibleReceiver)
- _receiverExpression = null
- implicitScope = type.scope(
- useSiteSession = useSiteSession,
- scopeSession = scopeSession,
- callableCopyTypeCalculator = CallableCopyTypeCalculator.DoNothing,
- requiredMembersPhase = FirResolvePhase.STATUS,
- )
- }
-
/*
* Should be called only in ImplicitReceiverStack
*/
diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt
index 19cec66..33b4d98 100644
--- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt
+++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt
@@ -14,10 +14,7 @@
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
-import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
-import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
-import org.jetbrains.kotlin.fir.resolve.substitution.NoSubstitutor
-import org.jetbrains.kotlin.fir.resolve.substitution.createTypeSubstitutorByTypeConstructor
+import org.jetbrains.kotlin.fir.resolve.substitution.*
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol
@@ -205,6 +202,10 @@
return this.intersectedTypes.any { it.containsInternal(predicate) }
}
+ if (this is ConeCapturedType) {
+ return this.constructor.projection.type?.containsInternal(predicate) == true
+ }
+
repeat(argumentsCount()) { index ->
val argument = getArgument(index)
if (!argument.isStarProjection() && argument.getType().containsInternal(predicate)) return true
@@ -358,6 +359,15 @@
return ConeSubstitutor.Empty
}
+ override fun createSubstitutionFromSubtypingStubTypesToTypeVariables(): TypeSubstitutorMarker {
+ return object : AbstractConeSubstitutor(this) {
+ override fun substituteType(type: ConeKotlinType): ConeKotlinType? {
+ return ((type as? ConeStubTypeForTypeVariableInSubtyping)
+ ?.constructor?.variable?.defaultType)?.withNullability(type.nullability, this@ConeInferenceContext)
+ }
+ }
+ }
+
override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker {
if (this === NoSubstitutor) return type
require(this is ConeSubstitutor)
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt
index 63aff9e..3fb8066 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt
@@ -26,8 +26,8 @@
import org.jetbrains.kotlin.fir.resolve.calls.tower.TowerGroup
import org.jetbrains.kotlin.fir.resolve.calls.tower.TowerResolveManager
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
-import org.jetbrains.kotlin.fir.resolve.inference.FirBuilderInferenceSession
import org.jetbrains.kotlin.fir.resolve.inference.ResolvedCallableReferenceAtom
+import org.jetbrains.kotlin.fir.resolve.inference.csBuilder
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer
@@ -398,10 +398,11 @@
}
fun resolveCallableReference(
- constraintSystemBuilder: ConstraintSystemBuilder,
+ containingCallCandidate: Candidate,
resolvedCallableReferenceAtom: ResolvedCallableReferenceAtom,
hasSyntheticOuterCall: Boolean,
- ): Pair<CandidateApplicability, Boolean> {
+ ): Pair<CandidateApplicability, Boolean> = components.context.inferenceSession.runCallableReferenceResolution(containingCallCandidate) {
+ val constraintSystemBuilder = containingCallCandidate.csBuilder
val callableReferenceAccess = resolvedCallableReferenceAtom.reference
val calleeReference = callableReferenceAccess.calleeReference
val lhs = resolvedCallableReferenceAtom.lhs
@@ -450,7 +451,7 @@
calleeReference.source
)
resolvedCallableReferenceAtom.resultingReference = errorReference
- return applicability to false
+ return@runCallableReferenceResolution applicability to false
}
reducedCandidates.size > 1 -> {
if (resolvedCallableReferenceAtom.hasBeenPostponed) {
@@ -460,10 +461,10 @@
calleeReference.source
)
resolvedCallableReferenceAtom.resultingReference = errorReference
- return applicability to false
+ return@runCallableReferenceResolution applicability to false
}
resolvedCallableReferenceAtom.hasBeenPostponed = true
- return applicability to true
+ return@runCallableReferenceResolution applicability to true
}
}
@@ -486,7 +487,7 @@
resolvedCallableReferenceAtom.resultingReference = reference
resolvedCallableReferenceAtom.resultingTypeForCallableReference = chosenCandidate.resultingTypeForCallableReference
- return applicability to true
+ return@runCallableReferenceResolution applicability to true
}
fun resolveDelegatingConstructorCall(
@@ -859,7 +860,7 @@
* can be important in builder inference mode, and it will never work if we skip completion here.
* See inferenceFromLambdaReturnStatement.kt test.
*/
- if (components.context.inferenceSession !is FirBuilderInferenceSession &&
+ if (!candidate.usedOuterCs &&
createResolvedReferenceWithoutCandidateForLocalVariables &&
explicitReceiver?.resolvedType !is ConeIntegerLiteralType &&
coneSymbol is FirVariableSymbol &&
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirOverloadByLambdaReturnTypeResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirOverloadByLambdaReturnTypeResolver.kt
index 790bff4..8d7e82f 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirOverloadByLambdaReturnTypeResolver.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirOverloadByLambdaReturnTypeResolver.kt
@@ -39,26 +39,6 @@
): Set<Candidate> where T : FirStatement, T : FirResolvable {
if (bestCandidates.size <= 1) return bestCandidates
- /*
- * Inference session may look into candidate of call, and for that it uses callee reference.
- * So we need replace reference with proper candidate before calling inference session
- */
- val shouldRunCompletion = if (inferenceSession != FirInferenceSession.DEFAULT) {
- var shouldRunCompletion = true
- val originalReference = qualifiedAccess.calleeReference
- for (candidate in bestCandidates) {
- qualifiedAccess.replaceCalleeReference(FirNamedReferenceWithCandidate(null, candidate.callInfo.name, candidate))
- shouldRunCompletion = shouldRunCompletion && inferenceSession.shouldRunCompletion(qualifiedAccess)
- if (!shouldRunCompletion) break
- }
- qualifiedAccess.replaceCalleeReference(originalReference)
- shouldRunCompletion
- } else {
- true
- }
-
- if (!shouldRunCompletion) return bestCandidates
-
return reduceCandidatesImpl(
qualifiedAccess,
bestCandidates,
@@ -137,7 +117,7 @@
firstCandidate.system,
firstAtom,
firstCandidate,
- ConstraintSystemCompletionMode.FULL,
+ forOverloadByLambdaReturnType = true,
)
while (iterator.hasNext()) {
val (candidate, atom) = iterator.next()
@@ -147,7 +127,6 @@
atom,
candidate,
results,
- ConstraintSystemCompletionMode.FULL,
)
}
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirSpecialTowerDataContexts.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirSpecialTowerDataContexts.kt
index 87dbcba..7884c61 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirSpecialTowerDataContexts.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirSpecialTowerDataContexts.kt
@@ -7,43 +7,54 @@
import org.jetbrains.kotlin.fir.declarations.FirTowerDataContext
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
+import org.jetbrains.kotlin.fir.resolve.inference.FirInferenceSession
import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousFunctionSymbol
class FirSpecialTowerDataContexts {
- private val towerDataContextForAnonymousFunctions: MutableMap<FirAnonymousFunctionSymbol, FirTowerDataContext> = mutableMapOf()
- private val towerDataContextForCallableReferences: MutableMap<FirCallableReferenceAccess, FirTowerDataContext> = mutableMapOf()
+ private val contextForAnonymousFunctions: MutableMap<FirAnonymousFunctionSymbol, PostponedAtomsResolutionContext> = mutableMapOf()
+ private val contextForCallableReferences: MutableMap<FirCallableReferenceAccess, PostponedAtomsResolutionContext> = mutableMapOf()
- fun getAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol): FirTowerDataContext? {
- return towerDataContextForAnonymousFunctions[symbol]
+ fun getAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol): PostponedAtomsResolutionContext? {
+ return contextForAnonymousFunctions[symbol]
}
- fun getCallableReferenceContext(access: FirCallableReferenceAccess): FirTowerDataContext? {
- return towerDataContextForCallableReferences[access]
+ fun getCallableReferenceContext(access: FirCallableReferenceAccess): PostponedAtomsResolutionContext? {
+ return contextForCallableReferences[access]
}
- fun storeAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol, context: FirTowerDataContext) {
- towerDataContextForAnonymousFunctions[symbol] = context
+ fun storeAnonymousFunctionContext(
+ symbol: FirAnonymousFunctionSymbol,
+ context: FirTowerDataContext,
+ inferenceSession: FirInferenceSession,
+ ) {
+ contextForAnonymousFunctions[symbol] = Pair(context, inferenceSession)
}
fun dropAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol) {
- towerDataContextForAnonymousFunctions.remove(symbol)
+ contextForAnonymousFunctions.remove(symbol)
}
- fun storeCallableReferenceContext(access: FirCallableReferenceAccess, context: FirTowerDataContext) {
- towerDataContextForCallableReferences[access] = context
+ fun storeCallableReferenceContext(
+ access: FirCallableReferenceAccess,
+ context: FirTowerDataContext,
+ inferenceSession: FirInferenceSession
+ ) {
+ contextForCallableReferences[access] = Pair(context, inferenceSession)
}
fun dropCallableReferenceContext(access: FirCallableReferenceAccess) {
- towerDataContextForCallableReferences.remove(access)
+ contextForCallableReferences.remove(access)
}
fun putAll(contexts: FirSpecialTowerDataContexts) {
- towerDataContextForCallableReferences.putAll(contexts.towerDataContextForCallableReferences)
- towerDataContextForAnonymousFunctions.putAll(contexts.towerDataContextForAnonymousFunctions)
+ contextForCallableReferences.putAll(contexts.contextForCallableReferences)
+ contextForAnonymousFunctions.putAll(contexts.contextForAnonymousFunctions)
}
fun clear() {
- towerDataContextForAnonymousFunctions.clear()
- towerDataContextForCallableReferences.clear()
+ contextForAnonymousFunctions.clear()
+ contextForCallableReferences.clear()
}
}
+
+typealias PostponedAtomsResolutionContext = Pair<FirTowerDataContext, FirInferenceSession>
\ No newline at end of file
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt
index 8af883c..723dc4c 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt
@@ -28,7 +28,11 @@
}
data object ContextIndependent : ResolutionMode(forceFullCompletion = true)
- data object ReceiverResolution : ResolutionMode(forceFullCompletion = true)
+
+ sealed class ReceiverResolution(val forCallableReference: Boolean) : ResolutionMode(forceFullCompletion = true) {
+ data object ForCallableReference : ReceiverResolution(forCallableReference = true)
+ companion object : ReceiverResolution(forCallableReference = false)
+ }
class WithExpectedType(
val expectedTypeRef: FirResolvedTypeRef,
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt
index cff3f46..c058961 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt
@@ -7,10 +7,12 @@
import org.jetbrains.kotlin.builtins.functions.isBasicFunctionOrKFunction
import org.jetbrains.kotlin.config.LanguageFeature
-import org.jetbrains.kotlin.fir.*
+import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.expressions.*
+import org.jetbrains.kotlin.fir.languageVersionSettings
+import org.jetbrains.kotlin.fir.lookupTracker
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.resolve.*
@@ -21,6 +23,7 @@
import org.jetbrains.kotlin.fir.resolve.inference.preprocessLambdaArgument
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolvedTypeDeclaration
+import org.jetbrains.kotlin.fir.returnExpressions
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.types.*
@@ -34,6 +37,7 @@
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext
+import org.jetbrains.kotlin.types.model.safeSubstitute
import org.jetbrains.kotlin.types.model.typeConstructor
val SAM_LOOKUP_NAME = Name.special("<SAM-CONSTRUCTOR>")
@@ -190,7 +194,7 @@
* placeholder type with value 0, but argument contains type with proper literal value
*/
val type: ConeKotlinType = context.returnTypeCalculator.tryCalculateReturnType(candidate.symbol.fir as FirCallableDeclaration).type
- val argumentType = candidate.substitutor.substituteOrSelf(type)
+ val argumentType = candidate.substitutor.substituteOrSelf(type).prepareTypeForPartiallyCompletedPCLACall(candidate, csBuilder)
resolvePlainArgumentType(
csBuilder,
argument,
@@ -204,6 +208,26 @@
)
}
+// For PCLA/Delegate inference partially completed calls, there might be a situation when
+// - There are already fixed variables (because we need them for lambda analysis)
+// - They are used in return types
+//
+// In this case, we substitute them explicitly because otherwise
+// TypeCheckerStateForConstraintInjector.fixedTypeVariable throws an exception.
+//
+// Note that this is not relevant outside PCLA context because
+// - For partial completion, we avoid fixing TVs that are used inside return types
+// - For FULL completion, we would run completion results writing, so there would be no candidates and type variables inside return types.
+//
+// See singleBranchConditionLastStatementInLambda.kt and assignmentUsingIncompletePCLACall.kt tests
+private fun ConeKotlinType.prepareTypeForPartiallyCompletedPCLACall(
+ candidate: Candidate, outerCSBuilder: ConstraintSystemBuilder
+): ConeKotlinType {
+ if (!candidate.system.usesOuterCs) return this
+
+ return outerCSBuilder.buildCurrentSubstitutor().safeSubstitute(candidate.callInfo.session.typeContext, this) as ConeKotlinType
+}
+
fun Candidate.resolvePlainExpressionArgument(
csBuilder: ConstraintSystemBuilder,
argument: FirExpression,
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt
index 88dbaf0..4537672 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt
@@ -24,6 +24,7 @@
ConstraintSystemForks,
CheckIncompatibleTypeVariableUpperBounds,
TypeParameterAsCallable,
+ TypeVariablesInExplicitReceivers,
)
object SyntheticSelect : CallKind(
@@ -59,6 +60,7 @@
ConstraintSystemForks,
CheckIncompatibleTypeVariableUpperBounds,
TypeParameterAsCallable,
+ TypeVariablesInExplicitReceivers,
)
object DelegatingConstructorCall : CallKind(
@@ -94,6 +96,7 @@
CheckIncompatibleTypeVariableUpperBounds,
ProcessDynamicExtensionAnnotation,
LowerPriorityIfDynamic,
+ TypeVariablesInExplicitReceivers,
)
object SyntheticIdForCallableReferencesResolution : CallKind(
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt
index 30304a4..7e794a6 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt
@@ -7,12 +7,12 @@
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.fakeElement
+import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
-import org.jetbrains.kotlin.fir.expressions.FirExpression
-import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression
-import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
+import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpressionCopy
import org.jetbrains.kotlin.fir.expressions.impl.FirExpressionStub
+import org.jetbrains.kotlin.fir.resolve.inference.FirInferenceSession
import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents
import org.jetbrains.kotlin.fir.resolve.inference.PostponedResolvedAtom
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
@@ -28,6 +28,7 @@
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.util.CodeFragmentAdjustment
+import org.jetbrains.kotlin.utils.addToStdlib.runUnless
class Candidate(
symbol: FirBasedSymbol<*>,
@@ -47,6 +48,7 @@
val isFromCompanionObjectTypeScope: Boolean = false,
// It's only true if we're in the member scope of smart cast receiver and this particular candidate came from original type
val isFromOriginalTypeInPresenceOfSmartCast: Boolean = false,
+ inferenceSession: FirInferenceSession,
) : AbstractCandidate() {
override var symbol: FirBasedSymbol<*> = symbol
@@ -66,10 +68,21 @@
this.symbol = symbol
}
+ val usedOuterCs: Boolean get() = system.usesOuterCs
+
private var systemInitialized: Boolean = false
val system: NewConstraintSystemImpl by lazy(LazyThreadSafetyMode.NONE) {
val system = constraintSystemFactory.createConstraintSystem()
- system.setBaseSystem(baseSystem)
+
+ val baseCSFromInferenceSession =
+ runUnless(baseSystem.usesOuterCs) { inferenceSession.baseConstraintStorageForCandidate(this) }
+ if (baseCSFromInferenceSession != null) {
+ system.setBaseSystem(baseCSFromInferenceSession)
+ system.addOtherSystem(baseSystem)
+ } else {
+ system.setBaseSystem(baseSystem)
+ }
+
systemInitialized = true
system
}
@@ -102,6 +115,9 @@
var functionTypesOfSamConversions: HashMap<FirExpression, ConeKotlinType>? = null
lateinit var typeArgumentMapping: TypeArgumentMapping
val postponedAtoms = mutableListOf<PostponedResolvedAtom>()
+ val postponedPCLACalls = mutableListOf<FirStatement>()
+ val lambdasAnalyzedWithPCLA = mutableListOf<FirAnonymousFunction>()
+ val onCompletionResultsWritingCallbacks = mutableListOf<(ConeSubstitutor) -> Unit>()
var currentApplicability = CandidateApplicability.RESOLVED
private set
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt
index fc39b97..41b85af 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt
@@ -35,10 +35,26 @@
companion object {
private fun buildBaseSystem(context: ResolutionContext, callInfo: CallInfo): ConstraintStorage {
val system = context.inferenceComponents.createConstraintSystem()
- system.addOuterSystem(context.bodyResolveContext.outerConstraintStorage)
- callInfo.arguments.forEach {
- system.addSubsystemFromExpression(it)
+
+ val argumentSubsystems = buildList {
+ callInfo.arguments.forEach {
+ processConstraintStorageFromExpression(it, this::add)
+ }
}
+
+ // If any of the arguments uses outer CS, then all of them effectively share it, so we might add the last one as
+ // containing the most up-to-date version.
+ val lastSubsystemWithOuterCs = argumentSubsystems.lastOrNull { it.usesOuterCs }
+ if (lastSubsystemWithOuterCs != null) {
+ system.setBaseSystem(lastSubsystemWithOuterCs)
+ }
+
+ argumentSubsystems.forEach {
+ if (!it.usesOuterCs) {
+ system.addOtherSystem(it)
+ }
+ }
+
return system.asReadOnlyStorage()
}
}
@@ -79,6 +95,7 @@
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> false
},
isFromOriginalTypeInPresenceOfSmartCast,
+ context.bodyResolveContext.inferenceSession,
)
// The counterpart in FE 1.0 checks if the given descriptor is VariableDescriptor yet not PropertyDescriptor.
@@ -165,6 +182,7 @@
baseSystem,
callInfo,
originScope = null,
+ inferenceSession = context.bodyResolveContext.inferenceSession,
)
}
@@ -194,25 +212,47 @@
}
}
-fun PostponedArgumentsAnalyzerContext.addSubsystemFromExpression(statement: FirStatement): Boolean {
+fun processConstraintStorageFromExpression(statement: FirStatement, processor: (ConstraintStorage) -> Unit): Boolean {
return when (statement) {
is FirQualifiedAccessExpression,
is FirWhenExpression,
is FirTryExpression,
is FirCheckNotNullCall,
- is FirElvisExpression -> {
+ is FirElvisExpression,
+ -> {
val candidate = (statement as FirResolvable).candidate() ?: return false
- addOtherSystem(candidate.system.asReadOnlyStorage())
+ processor(candidate.system.asReadOnlyStorage())
true
}
- is FirSafeCallExpression -> addSubsystemFromExpression(statement.selector)
- is FirWrappedArgumentExpression -> addSubsystemFromExpression(statement.expression)
- is FirBlock -> statement.returnExpressions().any { addSubsystemFromExpression(it) }
+ is FirSafeCallExpression -> processConstraintStorageFromExpression(statement.selector, processor)
+ is FirWrappedArgumentExpression -> processConstraintStorageFromExpression(statement.expression, processor)
+ is FirBlock -> {
+ var wasAny = false
+
+ // Might be `.any {` call, but we should process all the items
+ statement.returnExpressions().forEach {
+ if (processConstraintStorageFromExpression(it, processor)) {
+ wasAny = true
+ }
+ }
+
+ wasAny
+ }
else -> false
}
}
+fun PostponedArgumentsAnalyzerContext.addSubsystemFromExpression(statement: FirStatement): Boolean {
+ return processConstraintStorageFromExpression(statement) {
+ // If a call inside a lambda uses outer CS,
+ // it's already integrated into inference session via FirPCLAInferenceSession.processPartiallyResolvedCall
+ if (!it.usesOuterCs) {
+ addOtherSystem(it)
+ }
+ }
+}
+
internal fun FirResolvable.candidate(): Candidate? {
return when (val callee = this.calleeReference) {
is FirNamedReferenceWithCandidate -> return callee.candidate
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt
index aad0a70..36fdcc1 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt
@@ -123,21 +123,6 @@
candidate.chosenExtensionReceiver = receiver.expression
- val checkBuilderInferenceRestriction =
- !context.session.languageVersionSettings
- .supportsFeature(LanguageFeature.NoBuilderInferenceWithoutAnnotationRestriction)
- if (checkBuilderInferenceRestriction) {
- val resolvedType = receiver.expression.resolvedType
- if (resolvedType is ConeStubTypeForChainInference) {
- val typeVariable = resolvedType.constructor.variable
- sink.yieldDiagnostic(
- StubBuilderInferenceReceiver(
- (typeVariable.typeConstructor.originalTypeParameter as ConeTypeParameterLookupTag).typeParameterSymbol
- )
- )
- }
- }
-
sink.yieldIfNeed()
}
@@ -264,6 +249,39 @@
}
}
+object 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 extension receiver of $candidate"
+ }
+ }
+
+ private fun ConeKotlinType.obtainTypeVariable(): ConeTypeVariableType? = when (this) {
+ is ConeFlexibleType -> lowerBound.obtainTypeVariable()
+ is ConeTypeVariableType -> this
+ is ConeDefinitelyNotNullType -> original.obtainTypeVariable()
+ is ConeIntersectionType -> intersectedTypes.firstNotNullOfOrNull { it.obtainTypeVariable() }
+ else -> null
+ }
+}
+
private fun Candidate.findClosestMatchingReceivers(
expectedType: ConeKotlinType,
receiverGroups: List<List<FirExpression>>,
@@ -569,7 +587,7 @@
if (atom is ResolvedCallableReferenceAtom) {
val (applicability, success) =
context.bodyResolveComponents.callResolver.resolveCallableReference(
- candidate.csBuilder, atom, hasSyntheticOuterCall = candidate.callInfo.name == ACCEPT_SPECIFIC_TYPE.callableName
+ candidate, atom, hasSyntheticOuterCall = candidate.callInfo.name == ACCEPT_SPECIFIC_TYPE.callableName
)
if (!success) {
// If the resolution was unsuccessful, we ensure that an error will be reported for the callable reference
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt
index 3267f223..40016b4 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt
@@ -20,6 +20,7 @@
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
+import org.jetbrains.kotlin.fir.resolve.calls.candidate
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
@@ -30,7 +31,10 @@
import org.jetbrains.kotlin.fir.scopes.impl.toConeType
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
-import org.jetbrains.kotlin.fir.symbols.impl.*
+import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.StandardClassIds
@@ -926,6 +930,10 @@
private fun FirStatement.orderedArguments(callee: FirFunction): Array<out FirExpression?>? {
fun FirQualifiedAccessExpression.firstReceiver(): FirExpression? {
+ val candidate = candidate()
+ if (candidate != null) {
+ return candidate.chosenExtensionReceiverExpression() ?: candidate.dispatchReceiverExpression()
+ }
return extensionReceiver ?: dispatchReceiver
}
@@ -937,7 +945,7 @@
return when (this) {
is FirFunctionCall -> {
- val argumentToParameter = resolvedArgumentMapping ?: return null
+ val argumentToParameter = resolvedArgumentMapping ?: candidate()?.argumentMapping ?: return null
val parameterToArgument = argumentToParameter.entries.associate { it.value to it.key.unwrapArgument() }
Array(callee.valueParameters.size + 1) { i ->
if (i > 0) parameterToArgument[callee.valueParameters[i - 1]] else receiver
@@ -1154,7 +1162,9 @@
}
private fun exitBooleanNot(flow: MutableFlow, expression: FirFunctionCall) {
- val argumentVariable = variableStorage.get(flow, expression.dispatchReceiver!!) ?: return
+ val argumentVariable = variableStorage.get(flow,
+ expression.candidate()?.dispatchReceiverExpression() ?: expression.dispatchReceiver!!
+ ) ?: return
val expressionVariable = variableStorage.createSynthetic(expression)
// Alternatively: (expression == true => argument == false) && (expression == false => argument == true)
// Which implementation is faster and/or consumes less memory is an open question.
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt
index add0736..8d8b659 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt
@@ -23,10 +23,7 @@
import org.jetbrains.kotlin.fir.types.ConeErrorType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeVariable
-import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionContext
-import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
-import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDependencyInformationProvider
-import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator
+import org.jetbrains.kotlin.resolve.calls.inference.components.*
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
import org.jetbrains.kotlin.resolve.calls.inference.model.NotEnoughInformationForTypeParameter
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
@@ -81,9 +78,9 @@
if (analyzeArgumentWithFixedParameterTypes(languageVersionSettings, postponedArguments, analyze))
continue
- val isThereAnyReadyForFixationVariable = variableFixationFinder.findFirstVariableForFixation(
- this,
- getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms),
+ val isThereAnyReadyForFixationVariable = findFirstVariableForFixation(
+ collectVariablesFromContext,
+ topLevelAtoms,
postponedArguments,
completionMode,
topLevelType
@@ -113,7 +110,7 @@
if (wasBuiltNewExpectedTypeForSomeArgument)
continue
- if (completionMode == ConstraintSystemCompletionMode.FULL) {
+ if (completionMode.allLambdasShouldBeAnalyzed) {
// Stage 3: fix variables for parameter types of all postponed arguments
for (argument in postponedArguments) {
val variableWasFixed = postponedArgumentsInputTypesResolver.fixNextReadyVariableForParameterTypeIfNeeded(
@@ -152,21 +149,32 @@
continue
// Stage 7: try to complete call with the builder inference if there are uninferred type variables
- val allTypeVariables = getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms)
val areThereAppearedProperConstraintsForSomeVariable = tryToCompleteWithBuilderInference(
- completionMode, topLevelType, postponedArguments, allTypeVariables, analyze
+ completionMode, postponedArguments, analyze,
)
if (areThereAppearedProperConstraintsForSomeVariable)
continue
+ if (completionMode == ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL) {
+ // Complete all lambdas, maybe with semi-fixing type variables used as top-level input types
+ if (analyzeRemainingNotAnalyzedPostponedArgument(postponedArguments, analyze))
+ continue
+ }
+
// Stage 8: report "not enough information" for uninferred type variables
reportNotEnoughTypeInformation(
- completionMode, topLevelAtoms, topLevelType, allTypeVariables, postponedArguments
+ completionMode, topLevelAtoms, topLevelType, postponedArguments
)
+ if (completionMode == ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL) {
+ reportNotEnoughInformationForTypeVariablesRequiredForInputTypesOfLambdas(
+ postponedArguments, topLevelType, dependencyProvider, topLevelAtoms
+ )
+ }
+
// Stage 9: force analysis of remaining not analyzed postponed arguments and rerun stages if there are
- if (completionMode == ConstraintSystemCompletionMode.FULL) {
+ if (completionMode.allLambdasShouldBeAnalyzed) {
if (analyzeRemainingNotAnalyzedPostponedArgument(postponedArguments, analyze))
continue
}
@@ -175,6 +183,49 @@
}
}
+ 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`
*
@@ -183,12 +234,10 @@
*/
private fun ConstraintSystemCompletionContext.tryToCompleteWithBuilderInference(
completionMode: ConstraintSystemCompletionMode,
- topLevelType: ConeKotlinType,
postponedArguments: List<PostponedResolvedAtom>,
- allTypeVariables: List<TypeConstructorMarker>,
analyze: (PostponedResolvedAtom) -> Unit,
): Boolean {
- if (completionMode != ConstraintSystemCompletionMode.FULL) return false
+ if (!completionMode.allLambdasShouldBeAnalyzed) return false
// If we use the builder inference anyway (if the annotation is presented), then we are already analysed builder inference lambdas
if (!languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceOnlyIfNeeded)) return false
@@ -198,56 +247,17 @@
fun ResolvedLambdaAtom.notFixedInputTypeVariables(): List<TypeVariableTypeConstructorMarker> =
inputTypes.flatMap { it.extractTypeVariables() }.filter { it !in fixedTypeVariables }
- val checkForDangerousBuilderInference =
- !languageVersionSettings.supportsFeature(LanguageFeature.NoBuilderInferenceWithoutAnnotationRestriction)
-
- // Let's call builder lambda (BL) a lambda that has non-zero not fixed input type variables in
- // type arguments of it's input types
- // ex: MutableList<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()
+ var anyAnalyzed = false
for (argument in lambdaArguments) {
- val reallyHasBuilderInferenceAnnotation = argument.isCorrespondingParameterAnnotatedWithBuilderInference
-
val notFixedInputTypeVariables = argument.notFixedInputTypeVariables()
if (notFixedInputTypeVariables.isEmpty()) continue
-
- // we have dangerous inference situation
- // if lambda annotated with BuilderInference it's probably safe, due to type shape
- // otherwise report multi-lambda builder inference restriction diagnostic
- if (checkForDangerousBuilderInference && dangerousBuilderInferenceWithoutAnnotation && !reallyHasBuilderInferenceAnnotation) {
- for (variable in notFixedInputTypeVariables) {
- addError(AnonymousFunctionBasedMultiLambdaBuilderInferenceRestriction(argument.atom, variable.typeParameter!!))
- }
- }
-
- for (variable in notFixedInputTypeVariables) {
- builder.markPostponedVariable(notFixedTypeVariables.getValue(variable).typeVariable)
- }
-
analyze(argument)
+
+ anyAnalyzed = true
}
- val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
- this, allTypeVariables, postponedArguments, completionMode, topLevelType
- )
-
- // continue completion (rerun stages) only if ready for fixation variables with proper constraints have appeared
- // (after analysing a lambda with the builder inference)
- // otherwise we don't continue and report "not enough type information" error
- return variableForFixation?.hasProperConstraint == true
+ return anyAnalyzed
}
private fun transformToAtomWithNewFunctionExpectedType(
@@ -277,16 +287,12 @@
collectVariablesFromContext: Boolean,
postponedArguments: List<PostponedResolvedAtom>,
): Boolean {
- val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
- this,
- getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms),
- postponedArguments,
- completionMode,
- topLevelType
+ val variableForFixation = findFirstVariableForFixation(
+ collectVariablesFromContext, topLevelAtoms, postponedArguments, completionMode, topLevelType
) ?: return false
val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable)
- if (!variableForFixation.hasProperConstraint) return false
+ if (!variableForFixation.isReady) return false
fixVariable(this, variableWithConstraints)
@@ -297,19 +303,17 @@
completionMode: ConstraintSystemCompletionMode,
topLevelAtoms: List<FirStatement>,
topLevelType: ConeKotlinType,
- allTypeVariables: List<TypeConstructorMarker>,
postponedArguments: List<PostponedResolvedAtom>,
) {
while (true) {
- val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
- this, allTypeVariables, postponedArguments, completionMode, topLevelType,
- ) ?: break
-
- assert(!variableForFixation.hasProperConstraint) {
+ val variableForFixation =
+ findFirstVariableForFixation(false, topLevelAtoms, postponedArguments, completionMode, topLevelType)
+ ?: break
+ assert(!variableForFixation.isReady) {
"At this stage there should be no remaining variables with proper constraints"
}
- if (completionMode == ConstraintSystemCompletionMode.PARTIAL) break
+ if (completionMode != ConstraintSystemCompletionMode.FULL) break
val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable)
processVariableWhenNotEnoughInformation(variableWithConstraints, topLevelAtoms)
@@ -399,30 +403,9 @@
topLevelAtom.collectAllTypeVariables()
}
- checkNotFixedTypeVariablesCountConsistency(result)
-
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(
c: ConstraintSystemCompletionContext,
variableWithConstraints: VariableWithConstraints,
@@ -581,13 +564,21 @@
val candidate = (calleeReference as? FirNamedReferenceWithCandidate)?.candidate ?: return
processor(candidate)
+ val visited = mutableSetOf<FirStatement>()
+
for (atom in candidate.postponedAtoms) {
if (atom !is ResolvedLambdaAtom || !atom.analyzed) continue
atom.returnStatements.forEach {
+ visited += it
it.processAllContainingCallCandidates(processBlocks, processor)
}
}
+
+ for (call in candidate.postponedPCLACalls) {
+ if (!visited.add(call)) continue
+ call.processAllContainingCallCandidates(processBlocks, processor)
+ }
}
val Candidate.csBuilder: NewConstraintSystemImpl get() = system.getBuilder()
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt
deleted file mode 100644
index 45b0ad4..0000000
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt
+++ /dev/null
@@ -1,388 +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.buildAbstractResultingSubstitutor
-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
- }
-
- 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)
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt
index 7b2489d..24ca31d 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt
@@ -40,9 +40,8 @@
import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
+import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
-import org.jetbrains.kotlin.types.model.StubTypeMarker
-import org.jetbrains.kotlin.types.model.TypeVariableMarker
import org.jetbrains.kotlin.types.model.safeSubstitute
import org.jetbrains.kotlin.utils.addToStdlib.runIf
@@ -80,18 +79,11 @@
val completionMode = candidate.computeCompletionMode(
session.inferenceComponents, resolutionMode, initialType
).let {
- // The difference between `shouldAvoidFullCompletion` and `!shouldRunCompletion` is very subtle:
- // we don't run even partial completion for `!inferenceSession.shouldRunCompletion(call)`, while actually
- // do that for `shouldAvoidFullCompletion`
- //
- // As for implementations, `shouldRunCompletion` only works for Builder inference, while `shouldAvoidFullCompletion` is for
- // delegate inference where it's assumed to have partially completed intermediate calls.
- //
- // Ideally, we should get rid of `shouldRunCompletion` once Builder inference is rewritten (see KT-61041 for tracking)
- if (it == ConstraintSystemCompletionMode.FULL && inferenceSession.shouldAvoidFullCompletion(call))
- ConstraintSystemCompletionMode.PARTIAL
- else
- it
+ when {
+ it == ConstraintSystemCompletionMode.FULL ->
+ inferenceSession.customCompletionModeInsteadOfFull(call) ?: ConstraintSystemCompletionMode.FULL
+ else -> it
+ }
}
val analyzer = createPostponedArgumentsAnalyzer(transformer.resolutionContext)
@@ -101,35 +93,27 @@
return when (completionMode) {
ConstraintSystemCompletionMode.FULL -> {
- if (inferenceSession.shouldRunCompletion(call)) {
- runCompletionForCall(candidate, completionMode, call, initialType, analyzer)
- val finalSubstitutor = candidate.system.asReadOnlyStorage()
- .buildAbstractResultingSubstitutor(session.typeContext) as ConeSubstitutor
- val completedCall = call.transformSingle(
- FirCallCompletionResultsWriterTransformer(
- session, components.scopeSession, finalSubstitutor,
- components.returnTypeCalculator,
- session.typeApproximator,
- components.dataFlowAnalyzer,
- components.integerLiteralAndOperatorApproximationTransformer,
- components.samResolver,
- components.context,
- ),
- null
- )
- inferenceSession.addCompletedCall(completedCall, candidate)
- completedCall
- } else {
- inferenceSession.processPartiallyResolvedCall(call, resolutionMode)
- call
- }
+ runCompletionForCall(candidate, completionMode, call, initialType, analyzer)
+ val finalSubstitutor = candidate.system.asReadOnlyStorage()
+ .buildAbstractResultingSubstitutor(session.typeContext) as ConeSubstitutor
+ call.transformSingle(
+ FirCallCompletionResultsWriterTransformer(
+ session, components.scopeSession, finalSubstitutor,
+ components.returnTypeCalculator,
+ session.typeApproximator,
+ components.dataFlowAnalyzer,
+ components.integerLiteralAndOperatorApproximationTransformer,
+ components.samResolver,
+ components.context,
+ ),
+ null
+ )
}
- ConstraintSystemCompletionMode.PARTIAL -> {
+ ConstraintSystemCompletionMode.PARTIAL, ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL -> {
runCompletionForCall(candidate, completionMode, call, initialType, analyzer)
- if (inferenceSession is FirDelegatedPropertyInferenceSession) {
- inferenceSession.processPartiallyResolvedCall(call, resolutionMode)
- }
+
+ inferenceSession.processPartiallyResolvedCall(call, resolutionMode, completionMode)
call
}
@@ -183,8 +167,8 @@
completionMode: ConstraintSystemCompletionMode,
call: T,
initialType: ConeKotlinType,
- analyzer: PostponedArgumentsAnalyzer? = null
- ) where T : FirResolvable, T : FirStatement {
+ analyzer: PostponedArgumentsAnalyzer? = null,
+ ) where T : FirStatement {
@Suppress("NAME_SHADOWING")
val analyzer = analyzer ?: createPostponedArgumentsAnalyzer(transformer.resolutionContext)
completer.complete(
@@ -194,7 +178,7 @@
initialType,
transformer.resolutionContext
) {
- analyzer.analyze(candidate.system, it, candidate, completionMode)
+ analyzer.analyze(candidate.system, it, candidate)
}
}
@@ -251,8 +235,9 @@
contextReceivers: List<ConeKotlinType>,
parameters: List<ConeKotlinType>,
expectedReturnType: ConeKotlinType?,
- stubsForPostponedVariables: Map<TypeVariableMarker, StubTypeMarker>,
- candidate: Candidate
+ candidate: Candidate,
+ withPCLASession: Boolean,
+ forOverloadByLambdaReturnType: Boolean,
): ReturnArgumentsAnalysisResult {
val lambdaArgument: FirAnonymousFunction = lambdaAtom.atom
val needItParam = lambdaArgument.valueParameters.isEmpty() && parameters.size == 1
@@ -355,29 +340,36 @@
} ?: components.noExpectedType
)
- val builderInferenceSession = runIf(stubsForPostponedVariables.isNotEmpty()) {
- @Suppress("UNCHECKED_CAST")
- FirBuilderInferenceSession(
- lambdaArgument,
- transformer.resolutionContext,
- stubsForPostponedVariables as Map<ConeTypeVariable, ConeStubType>
- )
- }
+ var additionalConstraints: ConstraintStorage? = null
transformer.context.withAnonymousFunctionTowerDataContext(lambdaArgument.symbol) {
- if (builderInferenceSession != null) {
- transformer.context.withInferenceSession(builderInferenceSession) {
- lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef))
+ val pclaInferenceSession =
+ runIf(withPCLASession) {
+ candidate.lambdasAnalyzedWithPCLA += lambdaArgument
+
+ FirPCLAInferenceSession(candidate, session.inferenceComponents, transformer.context.returnTypeCalculator)
+ }
+
+ if (pclaInferenceSession != null) {
+ transformer.context.withInferenceSession(pclaInferenceSession) {
+ transformer.context.withOuterConstraintStorage(candidate.system.currentStorage()) {
+ lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef))
+ }
+
+ applyResultsToMainCandidate()
}
} else {
- lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef))
+ additionalConstraints =
+ transformer.context.inferenceSession.runLambdaCompletion(candidate, forOverloadByLambdaReturnType) {
+ lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef))
+ }
}
}
transformer.context.dropContextForAnonymousFunction(lambdaArgument)
val returnArguments = components.dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(lambdaArgument).map { it.expression }
- return ReturnArgumentsAnalysisResult(returnArguments, builderInferenceSession)
+ return ReturnArgumentsAnalysisResult(returnArguments, additionalConstraints)
}
}
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt
index df3986d..2d75766 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt
@@ -5,52 +5,84 @@
package org.jetbrains.kotlin.fir.resolve.inference
-import org.jetbrains.kotlin.fir.declarations.FirProperty
-import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
-import org.jetbrains.kotlin.fir.expressions.FirFunctionCallOrigin
-import org.jetbrains.kotlin.fir.expressions.FirResolvable
-import org.jetbrains.kotlin.fir.expressions.FirStatement
+import org.jetbrains.kotlin.fir.FirElement
+import org.jetbrains.kotlin.fir.expressions.*
+import org.jetbrains.kotlin.fir.render
+import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.resolve.calls.InferenceError
import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext
import org.jetbrains.kotlin.fir.resolve.calls.candidate
+import org.jetbrains.kotlin.fir.resolve.initialTypeOfCandidate
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
+import org.jetbrains.kotlin.fir.resolve.transformers.FirCallCompletionResultsWriterTransformer
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.typeContext
+import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.util.OperatorNameConventions
+import org.jetbrains.kotlin.utils.addIfNotNull
class FirDelegatedPropertyInferenceSession(
- val property: FirProperty,
- resolutionContext: ResolutionContext,
- private val postponedArgumentsAnalyzer: PostponedArgumentsAnalyzer,
-) : FirInferenceSessionForChainedResolve(resolutionContext) {
+ private val resolutionContext: ResolutionContext,
+ private val callCompleter: FirCallCompleter,
+ private val delegateExpression: FirExpression,
+) : FirInferenceSession() {
- private var currentConstraintSystem = components.session.inferenceComponents.createConstraintSystem()
+ 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)
+
val currentConstraintStorage: ConstraintStorage get() = currentConstraintSystem.currentStorage()
private val unitType: ConeClassLikeType = components.session.builtinTypes.unitType.type
+ private var wasCompletionRun = false
+
// TODO after PCLA (KT-59107):
// Outer system seems to be a property of a concrete call resolution and probably should be applied to concrete call resolution
override fun <R> onCandidatesResolution(call: FirFunctionCall, candidatesResolutionCallback: () -> R): R {
- return if (!call.isAnyOfDelegateOperators())
- candidatesResolutionCallback()
- else
- resolutionContext.bodyResolveContext.withOuterConstraintStorage(
- currentConstraintSystem.currentStorage(),
- candidatesResolutionCallback
- )
+ if (wasCompletionRun || !call.isAnyOfDelegateOperators()) return candidatesResolutionCallback()
+ requireCallIsDelegateOperator(call)
+
+ return resolutionContext.bodyResolveContext.withOuterConstraintStorage(
+ currentConstraintSystem.currentStorage(),
+ candidatesResolutionCallback
+ )
}
- override fun <T> shouldAvoidFullCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement =
- call.isAnyOfDelegateOperators()
+ override fun customCompletionModeInsteadOfFull(call: FirResolvable): ConstraintSystemCompletionMode? = when {
+ call.isAnyOfDelegateOperators() && !wasCompletionRun -> ConstraintSystemCompletionMode.PARTIAL
+ else -> null
+ }
- override fun <T> processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement {
- if (resolutionMode != ResolutionMode.ContextDependent.Delegate && !call.isAnyOfDelegateOperators()) return
+ override fun <T> processPartiallyResolvedCall(
+ call: T,
+ resolutionMode: ResolutionMode,
+ completionMode: ConstraintSystemCompletionMode,
+ ) where T : FirResolvable, T : FirStatement {
+ if (wasCompletionRun || !call.isAnyOfDelegateOperators()) return
+
+ requireCallIsDelegateOperator(call)
val candidate = call.candidate
@@ -61,31 +93,82 @@
val candidateSystem = candidate.system
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 =
- 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 {
- if (this !is FirFunctionCall || origin != FirFunctionCallOrigin.Operator) return false
- val name = calleeReference.name
- return name == OperatorNameConventions.PROVIDE_DELEGATE || name == OperatorNameConventions.GET_VALUE || name == OperatorNameConventions.SET_VALUE
+ override fun baseConstraintStorageForCandidate(candidate: Candidate): ConstraintStorage? =
+ resolutionContext.bodyResolveContext.outerConstraintStorage.takeIf { it !== ConstraintStorage.Empty }
+
+ fun completeSessionOrPostponeIfNonRoot(onCompletionResultsWriting: (ConeSubstitutor) -> Unit) {
+ check(!wasCompletionRun)
+ wasCompletionRun = true
+
+ parentConstraintSystem.addOtherSystem(currentConstraintStorage)
+
+ (nonTrivialParentSession as? FirPCLAInferenceSession)?.apply {
+ if (delegateCandidate != null) {
+ callCompleter.runCompletionForCall(
+ delegateCandidate,
+ ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL,
+ delegateExpression,
+ components.initialTypeOfCandidate(delegateCandidate)
+ )
+ }
+
+ integrateChildSession(
+ buildList {
+ addIfNotNull(delegateExpression)
+ partiallyResolvedCalls.mapTo(this) { it.first as FirStatement }
+ },
+ parentConstraintSystem.currentStorage(),
+ onCompletionResultsWriting,
+ )
+ return
+ }
+
+ val completedCalls = completeCandidatesForRootSession()
+
+ val finalSubstitutor = parentConstraintSystem.asReadOnlyStorage()
+ .buildAbstractResultingSubstitutor(components.session.typeContext) as ConeSubstitutor
+
+ val callCompletionResultsWriter = callCompleter.createCompletionResultsWriter(
+ finalSubstitutor,
+ // TODO: Get rid of the mode
+ mode = FirCallCompletionResultsWriterTransformer.Mode.DelegatedPropertyCompletion
+ )
+ completedCalls.forEach {
+ it.transformSingle(callCompletionResultsWriter, null)
+ }
+
+ onCompletionResultsWriting(finalSubstitutor)
}
- fun completeCandidates(): List<FirResolvable> {
- val commonSystem = currentConstraintSystem.apply { prepareForGlobalCompletion() }
+ private fun completeCandidatesForRootSession(): List<FirResolvable> {
+ val parentSystem = parentConstraintSystem.apply { prepareForGlobalCompletion() }
- val notCompletedCalls = partiallyResolvedCalls.mapNotNull { partiallyResolvedCall ->
- partiallyResolvedCall.first.takeIf { resolvable ->
- resolvable.candidate() != null
+ val notCompletedCalls =
+ buildList {
+ addIfNotNull(delegateExpression as? FirResolvable)
+ partiallyResolvedCalls.mapNotNullTo(this) { partiallyResolvedCall ->
+ partiallyResolvedCall.first.takeIf { resolvable ->
+ resolvable.candidate() != null
+ }
+ }
}
- }
resolutionContext.bodyResolveContext.withInferenceSession(DEFAULT) {
@Suppress("UNCHECKED_CAST")
components.callCompleter.completer.complete(
- commonSystem.asConstraintSystemCompleterContext(),
+ parentSystem.asConstraintSystemCompleterContext(),
ConstraintSystemCompletionMode.FULL,
notCompletedCalls as List<FirStatement>,
unitType, resolutionContext
@@ -100,29 +183,31 @@
}
found
}.candidate
- postponedArgumentsAnalyzer.analyze(
- commonSystem,
+ callCompleter.createPostponedArgumentsAnalyzer(resolutionContext).analyze(
+ parentSystem,
lambdaAtom,
containingCandidateForLambda,
- ConstraintSystemCompletionMode.FULL,
)
}
}
- for ((_, candidate) in partiallyResolvedCalls) {
- for (error in commonSystem.errors) {
+ for (candidate in notCompletedCalls.mapNotNull { it.candidate() }) {
+ for (error in parentSystem.errors) {
candidate.addDiagnostic(InferenceError(error))
}
}
return notCompletedCalls
}
+}
- fun createFinalSubstitutor(): ConeSubstitutor =
- currentConstraintSystem.asReadOnlyStorage()
- .buildAbstractResultingSubstitutor(components.session.typeContext) as ConeSubstitutor
+fun FirElement.isAnyOfDelegateOperators(): Boolean {
+ if (this is FirPropertyAccessExpression) {
+ val originalCall = this.candidate()?.callInfo?.callSite as? FirFunctionCall ?: return false
+ return originalCall.isAnyOfDelegateOperators()
+ }
- override fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {}
-
- 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
}
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt
index 73b4119..fb32c71 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt
@@ -5,52 +5,60 @@
package org.jetbrains.kotlin.fir.resolve.inference
+import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirResolvable
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.types.ConeKotlinType
-import org.jetbrains.kotlin.fir.types.ConeTypeVariableTypeConstructor
-import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
+import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
+import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
abstract class FirInferenceSession {
companion object {
- val DEFAULT: FirInferenceSession = object : FirStubInferenceSession() {}
+ val DEFAULT: FirInferenceSession = object : FirInferenceSession() {
+ override fun <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
+ }
- /**
- * 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
+ open fun <T> runCallableReferenceResolution(candidate: Candidate, block: () -> T): T = block()
- abstract fun <T> processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement
- abstract fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement
+ open fun customCompletionModeInsteadOfFull(
+ call: FirResolvable,
+ ): ConstraintSystemCompletionMode? = null
- abstract fun inferPostponedVariables(
- lambda: ResolvedLambdaAtom,
- constraintSystemBuilder: ConstraintSystemBuilder,
- completionMode: ConstraintSystemCompletionMode,
- candidate: Candidate
- ): Map<ConeTypeVariableTypeConstructor, ConeKotlinType>?
+ abstract fun <T> processPartiallyResolvedCall(
+ call: T,
+ resolutionMode: ResolutionMode,
+ completionMode: ConstraintSystemCompletionMode
+ ) where T : FirResolvable, T : FirStatement
open fun <R> onCandidatesResolution(call: FirFunctionCall, candidatesResolutionCallback: () -> R) = candidatesResolutionCallback()
-}
-abstract class FirStubInferenceSession : FirInferenceSession() {
- override fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true
+ open fun baseConstraintStorageForCandidate(candidate: Candidate): ConstraintStorage? = null
- 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
+ open fun addSubtypeConstraintIfCompatible(lowerType: ConeKotlinType, upperType: ConeKotlinType, element: FirElement) {}
}
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSessionForChainedResolve.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSessionForChainedResolve.kt
deleted file mode 100644
index eceadc7..0000000
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSessionForChainedResolve.kt
+++ /dev/null
@@ -1,43 +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
-import org.jetbrains.kotlin.types.model.KotlinTypeMarker
-import org.jetbrains.kotlin.types.model.TypeConstructorMarker
-import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
-import org.jetbrains.kotlin.types.model.safeSubstitute
-
-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()!!
-
- override fun clear() {
- partiallyResolvedCalls.clear()
- completedCalls.clear()
- }
-}
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirPCLAInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirPCLAInferenceSession.kt
new file mode 100644
index 0000000..cb4ebbc
--- /dev/null
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirPCLAInferenceSession.kt
@@ -0,0 +1,305 @@
+/*
+ * 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.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 customCompletionModeInsteadOfFull(
+ call: FirResolvable,
+ ): ConstraintSystemCompletionMode? = when {
+ call.candidate()?.usedOuterCs == true -> ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL
+ else -> null
+ }
+
+ 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
+ }
+ }
+
+ 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() ?: return
+ if (!candidate.usedOuterCs) 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
+ }
+ }
+
+ 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
+
+ resolutionMode.hashCode()
+ 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: Position
+ c.addEqualityConstraint(variable.defaultType(c), resultType, ConeFixVariableConstraintPosition(variable))
+
+ return Pair(coneTypeVariableTypeConstructor, resultType)
+ }
+
+ override fun baseConstraintStorageForCandidate(candidate: Candidate): ConstraintStorage? {
+ if (candidate.needsToBePostponed()) return currentCommonSystem.currentStorage()
+ return null
+ }
+
+ private fun FirExpression.isLambda(): Boolean {
+ if (this is FirWrappedArgumentExpression) return expression.isLambda()
+ 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.isLambda() }) 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() }) 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.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 {
+ it is ConeTypeVariableType && it.typeConstructor in currentCommonSystem.allTypeVariables
+ }
+
+ override fun addSubtypeConstraintIfCompatible(lowerType: ConeKotlinType, upperType: ConeKotlinType, element: FirElement) {
+ currentCommonSystem.addSubtypeConstraintIfCompatible(lowerType, upperType, ConeExpectedTypeConstraintPosition)
+ }
+}
+
+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) }
+ }
+}
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt
index 2012422..902396b 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt
@@ -19,13 +19,16 @@
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzerContext
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
-import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
-import org.jetbrains.kotlin.resolve.calls.inference.model.BuilderInferencePosition
-import org.jetbrains.kotlin.types.model.*
+import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
+import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
+import org.jetbrains.kotlin.types.model.KotlinTypeMarker
+import org.jetbrains.kotlin.types.model.TypeConstructorMarker
+import org.jetbrains.kotlin.types.model.freshTypeConstructor
+import org.jetbrains.kotlin.types.model.safeSubstitute
data class ReturnArgumentsAnalysisResult(
val returnArguments: Collection<FirExpression>,
- val inferenceSession: FirInferenceSession?
+ val additionalConstraints: ConstraintStorage?,
)
interface LambdaAnalyzer {
@@ -35,8 +38,9 @@
contextReceivers: List<ConeKotlinType>,
parameters: List<ConeKotlinType>,
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
}
@@ -44,21 +48,24 @@
private val resolutionContext: ResolutionContext,
private val lambdaAnalyzer: LambdaAnalyzer,
private val components: InferenceComponents,
- private val callResolver: FirCallResolver
+ private val callResolver: FirCallResolver,
) {
fun analyze(
c: PostponedArgumentsAnalyzerContext,
argument: PostponedResolvedAtom,
candidate: Candidate,
- completionMode: ConstraintSystemCompletionMode
) {
when (argument) {
is ResolvedLambdaAtom ->
- analyzeLambda(c, argument, candidate, completionMode)
+ analyzeLambda(c, argument, candidate, forOverloadByLambdaReturnType = false)
is LambdaWithTypeVariableAsExpectedTypeAtom ->
- analyzeLambda(c, argument.transformToResolvedLambda(c.getBuilder(), resolutionContext), candidate, completionMode)
+ analyzeLambda(
+ c,
+ argument.transformToResolvedLambda(c.getBuilder(), resolutionContext),
+ candidate, forOverloadByLambdaReturnType = false
+ )
is ResolvedCallableReferenceAtom -> processCallableReference(argument, candidate)
}
@@ -66,7 +73,7 @@
private fun processCallableReference(atom: ResolvedCallableReferenceAtom, candidate: Candidate) {
if (atom.mightNeedAdditionalResolution) {
- callResolver.resolveCallableReference(candidate.csBuilder, atom, hasSyntheticOuterCall = false)
+ callResolver.resolveCallableReference(candidate, atom, hasSyntheticOuterCall = false)
}
val callableReferenceAccess = atom.reference
@@ -98,17 +105,30 @@
c: PostponedArgumentsAnalyzerContext,
lambda: ResolvedLambdaAtom,
candidate: Candidate,
- completionMode: ConstraintSystemCompletionMode,
+ forOverloadByLambdaReturnType: Boolean,
//diagnosticHolder: KotlinDiagnosticsHolder
): ReturnArgumentsAnalysisResult {
// TODO: replace with `require(!lambda.analyzed)` when KT-54767 will be fixed
if (lambda.analyzed) {
- return ReturnArgumentsAnalysisResult(lambda.returnStatements, inferenceSession = null)
+ return ReturnArgumentsAnalysisResult(lambda.returnStatements, additionalConstraints = null)
}
+ val additionalBindings: Map<TypeConstructorMarker, KotlinTypeMarker>? =
+ (resolutionContext.bodyResolveContext.inferenceSession as? FirPCLAInferenceSession)?.let { builderInferenceSession ->
+ // TODO: context receivers
+ buildMap {
+ lambda.receiver
+ ?.let { builderInferenceSession.fixVariablesForMemberScope(it, candidate.system) }
+ ?.let(this::plusAssign)
+
+ for (parameterType in lambda.parameters) {
+ builderInferenceSession.fixVariablesForMemberScope(parameterType, candidate.system)?.let(this::plusAssign)
+ }
+ }
+ }
+
val unitType = components.session.builtinTypes.unitType.type
- val stubsForPostponedVariables = c.bindingStubsForPostponedVariables()
- val currentSubstitutor = c.buildCurrentSubstitutor(stubsForPostponedVariables.mapKeys { it.key.freshTypeConstructor(c) })
+ val currentSubstitutor = c.buildCurrentSubstitutor(additionalBindings ?: emptyMap()) as ConeSubstitutor
fun substitute(type: ConeKotlinType) = currentSubstitutor.safeSubstitute(c, type) as ConeKotlinType
@@ -126,21 +146,27 @@
else -> null
}
+ val withPCLASession =
+ lambda.inputTypes
+ .any { inputType ->
+ with(c) { inputType.extractTypeVariables() }.any(c.notFixedTypeVariables::contains)
+ }
+
val results = lambdaAnalyzer.analyzeAndGetLambdaReturnArguments(
lambda,
receiver,
contextReceivers,
parameters,
expectedTypeForReturnArguments,
- stubsForPostponedVariables,
- candidate
+ candidate,
+ withPCLASession,
+ forOverloadByLambdaReturnType,
)
applyResultsOfAnalyzedLambdaToCandidateSystem(
c,
lambda,
candidate,
results,
- completionMode,
::substitute
)
return results
@@ -151,10 +177,13 @@
lambda: ResolvedLambdaAtom,
candidate: Candidate,
results: ReturnArgumentsAnalysisResult,
- completionMode: ConstraintSystemCompletionMode,
- substitute: (ConeKotlinType) -> ConeKotlinType = c.createSubstituteFunctorForLambdaAnalysis()
+ substitute: (ConeKotlinType) -> ConeKotlinType = c.createSubstituteFunctorForLambdaAnalysis(),
) {
- val (returnArguments, inferenceSession) = results
+ val (returnArguments, additionalConstraintStorage) = results
+
+ if (additionalConstraintStorage != null) {
+ c.addOtherSystem(additionalConstraintStorage)
+ }
val checkerSink: CheckerSink = CheckerSinkImpl(candidate)
val builder = c.getBuilder()
@@ -169,11 +198,6 @@
// If the lambda returns Unit, the last expression is not returned and should not be constrained.
val isLastExpression = it == lastExpression
- // Don't add last call for builder-inference
- // Note, that we don't use the same condition "(returnTypeRef.type.isUnitOrFlexibleUnit || lambda.atom.shouldReturnUnit(returnArguments))"
- // as in the if below, because "lambda.atom.shouldReturnUnit(returnArguments)" might mean that the last statement is not completed
- if (isLastExpression && lambdaExpectedTypeIsUnit && inferenceSession is FirBuilderInferenceSession) return@forEach
-
// TODO (KT-55837) questionable moment inherited from FE1.0 (the `haveSubsystem` case):
// fun <T> foo(): T
// run {
@@ -181,11 +205,25 @@
// foo() // T = Unit, even though there is no implicit return
// }
// Things get even weirder if T has an upper bound incompatible with Unit.
- // Not calling `addSubsystemFromExpression` for builder-inference is crucial
val haveSubsystem = c.addSubsystemFromExpression(it)
- if (isLastExpression && !haveSubsystem &&
- (lambdaExpectedTypeIsUnit || lambda.atom.shouldReturnUnit(returnArguments))
- ) return@forEach
+ val isUnitLambda = lambdaExpectedTypeIsUnit || lambda.atom.shouldReturnUnit(returnArguments)
+ if (isLastExpression && isUnitLambda) {
+ // That "if" is necessary because otherwise we would force a lambda return type
+ // to be inferred from completed last expression.
+ // See `test1` at testData/diagnostics/tests/inference/coercionToUnit/afterBareReturn.kt
+ if (haveSubsystem) {
+ // We don't force it because of the cases like
+ // buildMap {
+ // put("a", 1) // While `put` returns V, we should not enforce the latter to be a subtype of Unit
+ // }
+ // See KT-63602 for details.
+ builder.addSubtypeConstraintIfCompatible(
+ it.resolvedType, returnTypeRef.type,
+ ConeLambdaArgumentConstraintPosition(lambda.atom)
+ )
+ }
+ return@forEach
+ }
hasExpressionInReturnArguments = true
if (!builder.hasContradiction) {
@@ -212,25 +250,6 @@
lambda.analyzed = true
lambda.returnStatements = returnArguments
c.resolveForkPointsConstraints()
-
- if (inferenceSession != null) {
- val postponedVariables = inferenceSession.inferPostponedVariables(lambda, builder, completionMode, candidate)
-
- if (postponedVariables == null) {
- builder.removePostponedVariables()
- return
- }
-
- for ((constructor, resultType) in postponedVariables) {
- val variableWithConstraints = builder.currentStorage().notFixedTypeVariables[constructor] ?: continue
- val variable = variableWithConstraints.typeVariable as ConeTypeVariable
-
- builder.unmarkPostponedVariable(variable)
- builder.addSubtypeConstraint(resultType, variable.defaultType(c), BuilderInferencePosition)
- }
-
- c.removePostponedTypeVariablesFromConstraints(postponedVariables.keys)
- }
}
fun PostponedArgumentsAnalyzerContext.createSubstituteFunctorForLambdaAnalysis(): (ConeKotlinType) -> ConeKotlinType {
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt
index d116326..b994efc 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt
@@ -9,6 +9,7 @@
import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
+import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.utils.isInline
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
@@ -22,6 +23,7 @@
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
+import org.jetbrains.kotlin.fir.resolve.inference.FirStubTypeTransformer
import org.jetbrains.kotlin.fir.resolve.inference.ResolvedLambdaAtom
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
@@ -32,7 +34,10 @@
import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperator
import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperatorForUnsignedType
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
-import org.jetbrains.kotlin.fir.symbols.impl.*
+import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildStarProjection
@@ -124,7 +129,7 @@
if (mode == Mode.DelegatedPropertyCompletion) {
// Update type for `$delegateField` in `$$delegateField.get/setValue()` calls inside accessors
- val typeUpdater = TypeUpdaterForDelegateArguments()
+ val typeUpdater = TypeUpdaterForPCLAAndDelegateReceivers()
qualifiedAccessExpression.transformExplicitReceiver(typeUpdater, null)
}
@@ -137,6 +142,12 @@
extensionReceiver = extensionReceiver?.transformSingle(integerOperatorApproximator, expectedExtensionReceiverType)
}
+ if (subCandidate.usedOuterCs) {
+ val updaterForThisReferences = TypeUpdaterForPCLAAndDelegateReceivers()
+ dispatchReceiver = dispatchReceiver?.transformSingle(updaterForThisReferences, null)
+ extensionReceiver = extensionReceiver?.transformSingle(updaterForThisReferences, null)
+ }
+
qualifiedAccessExpression.apply {
replaceCalleeReference(calleeReference.toResolvedReference())
replaceDispatchReceiver(dispatchReceiver)
@@ -171,6 +182,21 @@
if (declaration !is FirErrorFunction) {
qualifiedAccessExpression.replaceTypeArguments(typeArguments)
}
+
+ for (postponedCall in subCandidate.postponedPCLACalls) {
+ postponedCall.transformSingle(this, null)
+ }
+
+ for (callback in subCandidate.onCompletionResultsWritingCallbacks) {
+ callback(finalSubstitutor)
+ }
+
+ // TODO: Be aware of exponent
+ val firStubTypeTransformer = FirStubTypeTransformer(finalSubstitutor)
+ for (lambda in subCandidate.lambdasAnalyzedWithPCLA) {
+ lambda.transformSingle(firStubTypeTransformer, null)
+ }
+
session.lookupTracker?.recordTypeResolveAsLookup(type, qualifiedAccessExpression.source, context.file.source)
return qualifiedAccessExpression
}
@@ -195,7 +221,7 @@
* TODO: In future, it would be nice to get rid of it and there's actually a way to do it – not using substitution overrides (see KT-61618)
*/
private fun Candidate.updateSubstitutedMemberIfReceiverContainsTypeVariable() {
- val updatedSymbol = symbol.updateSubstitutedMemberIfReceiverContainsTypeVariable() ?: return
+ val updatedSymbol = symbol.updateSubstitutedMemberIfReceiverContainsTypeVariable(usedOuterCs) ?: return
val oldSymbol = symbol
@OptIn(Candidate.UpdatingSymbol::class)
@@ -221,9 +247,9 @@
}
}
- private fun FirBasedSymbol<*>.updateSubstitutedMemberIfReceiverContainsTypeVariable(): FirBasedSymbol<*>? {
+ private fun FirBasedSymbol<*>.updateSubstitutedMemberIfReceiverContainsTypeVariable(usedOuterCs: Boolean): FirBasedSymbol<*>? {
// TODO: Add assertion that this function returns not-null only for BI and delegation inference
- if (mode != Mode.DelegatedPropertyCompletion) return null
+ if (mode != Mode.DelegatedPropertyCompletion && !usedOuterCs) return null
val fir = fir
if (fir !is FirCallableDeclaration) return null
@@ -240,6 +266,22 @@
) as? FirClassSubstitutionScope ?: return null
val original = fir.originalForSubstitutionOverride ?: return null
+
+ if (fir is FirSyntheticProperty && fir.symbol is FirSimpleSyntheticPropertySymbol && original is FirSyntheticProperty) {
+ var result: FirBasedSymbol<*>? = null
+ FirSyntheticPropertiesScope.createIfSyntheticNamesProviderIsDefined(session, updatedDispatchReceiverType, scope)
+ ?.processPropertiesByName(fir.name) {
+ val newProperty = it.fir as? FirSyntheticProperty ?: return@processPropertiesByName
+ val originalForNew = newProperty.originalForSubstitutionOverride ?: return@processPropertiesByName
+ if (originalForNew.getter.delegate == original.getter.delegate) {
+ check(result == null)
+ result = it
+ }
+ }
+
+ return result ?: error("Not found synthetic property: ${fir.renderWithType()}")
+ }
+
return findSingleSubstitutedSymbolWithOriginal(original.symbol) { processor ->
when (original) {
is FirSimpleFunction -> scope.processFunctionsByName(original.name, processor)
@@ -519,10 +561,19 @@
}
}
+ var dispatchReceiver = subCandidate.dispatchReceiverExpression()
+ var extensionReceiver = subCandidate.chosenExtensionReceiverExpression()
+
+ if (subCandidate.usedOuterCs) {
+ val updaterForThisReferences = TypeUpdaterForPCLAAndDelegateReceivers()
+ dispatchReceiver = dispatchReceiver?.transformSingle(updaterForThisReferences, null)
+ extensionReceiver = extensionReceiver?.transformSingle(updaterForThisReferences, null)
+ }
+
return callableReferenceAccess.apply {
replaceCalleeReference(resolvedReference)
- replaceDispatchReceiver(subCandidate.dispatchReceiverExpression())
- replaceExtensionReceiver(subCandidate.chosenExtensionReceiverExpression())
+ replaceDispatchReceiver(dispatchReceiver)
+ replaceExtensionReceiver(extensionReceiver)
if (calleeReference.candidate.doesResolutionResultOverrideOtherToPreserveCompatibility()) {
addNonFatalDiagnostic(ConeResolutionResultOverridesOtherToPreserveCompatibility)
}
@@ -533,25 +584,33 @@
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 {
return element
}
+ override fun transformThisReceiverExpression(thisReceiverExpression: FirThisReceiverExpression, data: Any?): FirStatement {
+ return transformTypeRefForQualifiedAccess(thisReceiverExpression)
+ }
+
override fun transformQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression,
data: Any?,
): FirStatement {
+ return transformTypeRefForQualifiedAccess(qualifiedAccessExpression)
+ }
+
+ override fun transformPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression, data: Any?): FirStatement {
+ return transformQualifiedAccessExpression(propertyAccessExpression, data)
+ }
+
+ private fun transformTypeRefForQualifiedAccess(qualifiedAccessExpression: FirQualifiedAccessExpression): FirQualifiedAccessExpression {
val originalType = qualifiedAccessExpression.resolvedType
val substitutedReceiverType = finallySubstituteOrNull(originalType) ?: return qualifiedAccessExpression
qualifiedAccessExpression.replaceConeTypeOrNull(substitutedReceiverType)
session.lookupTracker?.recordTypeResolveAsLookup(substitutedReceiverType, qualifiedAccessExpression.source, context.file.source)
return qualifiedAccessExpression
}
-
- override fun transformPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression, data: Any?): FirStatement {
- return transformQualifiedAccessExpression(propertyAccessExpression, data)
- }
}
private fun FirTypeRef.substitute(candidate: Candidate): ConeKotlinType {
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt
index 0b465f4..d6a5953 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt
@@ -20,12 +20,9 @@
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitExtensionReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.InaccessibleImplicitReceiverValue
-import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext
import org.jetbrains.kotlin.fir.resolve.dfa.DataFlowAnalyzerContext
-import org.jetbrains.kotlin.fir.resolve.inference.FirBuilderInferenceSession
-import org.jetbrains.kotlin.fir.resolve.inference.FirCallCompleter
-import org.jetbrains.kotlin.fir.resolve.inference.FirDelegatedPropertyInferenceSession
import org.jetbrains.kotlin.fir.resolve.inference.FirInferenceSession
+import org.jetbrains.kotlin.fir.resolve.inference.FirPCLAInferenceSession
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
import org.jetbrains.kotlin.fir.resolve.transformers.withScopeCleanup
import org.jetbrains.kotlin.fir.scopes.FirScope
@@ -57,6 +54,7 @@
@PrivateForInline
var regularTowerDataContexts = FirRegularTowerDataContexts(regular = FirTowerDataContext())
+ // TODO: Rename to postponed
@PrivateForInline
val specialTowerDataContexts = FirSpecialTowerDataContexts()
@@ -263,7 +261,6 @@
holder.scopeSession
)
addReceiver(labelName, receiver, additionalLabelName)
- (inferenceSession as? FirBuilderInferenceSession)?.addLambdaImplicitReceiver(receiver)
}
f()
@@ -331,11 +328,11 @@
}
@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
this.inferenceSession = inferenceSession
return try {
- block()
+ inferenceSession.block()
} finally {
this.inferenceSession = oldSession
}
@@ -352,10 +349,17 @@
}
@PrivateForInline
- inline fun <T> withTemporaryRegularContext(newContext: FirTowerDataContext?, f: () -> T): T {
- if (newContext == null) return f()
+ inline fun <T> withTemporaryRegularContext(newContext: PostponedAtomsResolutionContext?, f: () -> T): T {
+ val (towerDataContext, newInferenceSession) = newContext ?: return f()
+
return withTowerDataModeCleanup {
- withTowerDataContexts(regularTowerDataContexts.replaceAndSetActiveRegularContext(newContext), f)
+ withTowerDataContexts(regularTowerDataContexts.replaceAndSetActiveRegularContext(towerDataContext)) {
+ if (newInferenceSession !== this.inferenceSession) {
+ withInferenceSession(newInferenceSession) { f() }
+ } else {
+ f()
+ }
+ }
}
}
@@ -382,9 +386,11 @@
// to use information from local class inside it.
// However, we should not copy other kinds of inference sessions,
// otherwise we can "inherit" type variables from there provoking inference problems
- if (this@BodyResolveContext.inferenceSession is FirBuilderInferenceSession) {
+ if (this@BodyResolveContext.inferenceSession is FirPCLAInferenceSession) {
inferenceSession = this@BodyResolveContext.inferenceSession
}
+
+ outerConstraintStorage = this@BodyResolveContext.outerConstraintStorage
}
// withElement PUBLIC API
@@ -756,7 +762,9 @@
@OptIn(PrivateForInline::class)
fun storeContextForAnonymousFunction(anonymousFunction: FirAnonymousFunction) {
- specialTowerDataContexts.storeAnonymousFunctionContext(anonymousFunction.symbol, towerDataContext)
+ specialTowerDataContexts.storeAnonymousFunctionContext(
+ anonymousFunction.symbol, towerDataContext, inferenceSession
+ )
}
@OptIn(PrivateForInline::class)
@@ -867,23 +875,6 @@
}
}
- 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,
@@ -956,7 +947,8 @@
fun storeCallableReferenceContext(callableReferenceAccess: FirCallableReferenceAccess) {
specialTowerDataContexts.storeCallableReferenceContext(
callableReferenceAccess,
- towerDataContext.createSnapshot(keepMutable = false)
+ towerDataContext.createSnapshot(keepMutable = false),
+ inferenceSession,
)
}
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt
index fab1578..5f26389 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt
@@ -38,7 +38,6 @@
import org.jetbrains.kotlin.fir.resolve.inference.extractLambdaInfoFromFunctionType
import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
-import org.jetbrains.kotlin.fir.resolve.transformers.FirCallCompletionResultsWriterTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.FirStatusResolver
import org.jetbrains.kotlin.fir.resolve.transformers.contracts.runContractResolveForFunction
import org.jetbrains.kotlin.fir.resolve.transformers.transformVarargTypeToArrayType
@@ -329,89 +328,82 @@
private fun transformPropertyAccessorsWithDelegate(
property: FirProperty,
- delegate: FirExpression,
+ delegateContainer: FirExpression,
shouldResolveEverything: Boolean,
) {
- val isImplicitTypedProperty = property.returnTypeRef is FirImplicitTypeRef
+ require(delegateContainer is FirWrappedDelegateExpression)
+ dataFlowAnalyzer.enterDelegateExpression()
- context.forPropertyDelegateAccessors(property, resolutionContext, callCompleter) {
- dataFlowAnalyzer.enterDelegateExpression()
- // Resolve delegate expression, after that, delegate will contain either expr.provideDelegate or expr
+ // First, resolve delegate expression in dependent context withing existing (possibly Default) inference session
+ val delegateExpression =
+ // Resolve delegate expression; after that, delegate will contain either expr.provideDelegate or expr
if (property.isLocal) {
- property.transformDelegate(transformer, ResolutionMode.ContextDependent.Delegate)
+ transformDelegateExpression(delegateContainer)
} else {
context.forPropertyInitializer {
- property.transformDelegate(transformer, ResolutionMode.ContextDependent.Delegate)
+ transformDelegateExpression(delegateContainer)
}
}
- // We don't use inference from setValue calls (i.e. don't resolve setters until the delegate inference is completed),
- // when property doesn't have explicit type.
+ // Then, create a root/child inference session based on the resolved delegate expression
+ context.withInferenceSession(
+ FirDelegatedPropertyInferenceSession(
+ resolutionContext,
+ callCompleter,
+ delegateExpression,
+ )
+ ) {
+ property.replaceDelegate(
+ getResolvedProvideDelegateIfSuccessful(delegateContainer.provideDelegateCall, delegateExpression)
+ ?: delegateExpression
+ )
+
+ // We don't use inference from setValue calls (i.e., don't resolve setters until the delegate inference is completed)
+ // when the property doesn't have an explicit type.
// It's necessary because we need to supply the property type as the 3rd argument for `setValue` and there might be uninferred
// variables from `getValue`.
// The same logic was used at K1 (see org.jetbrains.kotlin.resolve.DelegatedPropertyResolver.inferDelegateTypeFromGetSetValueMethods)
+ val isImplicitTypedProperty = property.returnTypeRef is FirImplicitTypeRef
property.transformAccessors(
if (isImplicitTypedProperty) SetterResolutionMode.SKIP else SetterResolutionMode.FULLY_RESOLVE,
shouldResolveEverything,
)
- val completedCalls = completeCandidates()
-
- val finalSubstitutor = createFinalSubstitutor()
-
- finalSubstitutor.substituteOrNull(property.returnTypeRef.coneType)?.let { substitutedType ->
- property.replaceReturnTypeRef(property.returnTypeRef.withReplacedConeType(substitutedType))
- }
- property.getter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true)
- property.setter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true)
- property.replaceReturnTypeRef(
- property.returnTypeRef.approximateDeclarationType(
- session,
- property.visibilityForApproximation(),
- property.isLocal
+ completeSessionOrPostponeIfNonRoot { finalSubstitutor ->
+ finalSubstitutor.substituteOrNull(property.returnTypeRef.coneType)?.let { substitutedType ->
+ property.replaceReturnTypeRef(property.returnTypeRef.withReplacedConeType(substitutedType))
+ }
+ property.getter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true)
+ property.setter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true)
+ property.replaceReturnTypeRef(
+ property.returnTypeRef.approximateDeclarationType(
+ session,
+ property.visibilityForApproximation(),
+ property.isLocal
+ )
)
- )
- val callCompletionResultsWriter = callCompleter.createCompletionResultsWriter(
- finalSubstitutor,
- mode = FirCallCompletionResultsWriterTransformer.Mode.DelegatedPropertyCompletion
- )
- completedCalls.forEach {
- it.transformSingle(callCompletionResultsWriter, null)
+ // `isImplicitTypedProperty` means we haven't run setter resolution yet (see its second usage)
+ if (isImplicitTypedProperty) {
+ property.resolveSetter(mayResolveSetterBody = true, shouldResolveEverything = shouldResolveEverything)
+ }
+
+ dataFlowAnalyzer.exitDelegateExpression(delegateContainer)
}
}
-
- // `isImplicitTypedProperty` means we haven't run setter resolution yet (see its second usage)
- if (isImplicitTypedProperty) {
- property.resolveSetter(mayResolveSetterBody = true, shouldResolveEverything = shouldResolveEverything)
- }
-
- dataFlowAnalyzer.exitDelegateExpression(delegate)
}
- override fun transformPropertyAccessor(propertyAccessor: FirPropertyAccessor, data: ResolutionMode): FirPropertyAccessor {
- return propertyAccessor.also {
- transformProperty(it.propertySymbol.fir, data)
- }
- }
-
- override fun transformWrappedDelegateExpression(
- wrappedDelegateExpression: FirWrappedDelegateExpression,
- data: ResolutionMode,
- ): FirStatement {
- // First, resolve delegate expression in dependent context, and add potentially partially resolved call to inference session
- // (that is why we use ResolutionMode.ContextDependent.Delegate instead of plain ContextDependent)
- val delegateExpression = wrappedDelegateExpression.expression.transformSingle(transformer, ResolutionMode.ContextDependent.Delegate)
- .transformSingle(components.integerLiteralAndOperatorApproximationTransformer, null)
-
- val provideDelegateCall = wrappedDelegateExpression.provideDelegateCall
- provideDelegateCall.replaceExplicitReceiver(delegateExpression)
+ private fun getResolvedProvideDelegateIfSuccessful(
+ provideDelegateCall: FirFunctionCall,
+ resolvedDelegateExpression: FirExpression,
+ ): FirFunctionCall? {
+ provideDelegateCall.replaceExplicitReceiver(resolvedDelegateExpression)
// Resolve call for provideDelegate, without completion
// TODO: this generates some nodes in the control flow graph which we don't want if we
// end up not selecting this option, KT-59684
transformer.expressionsTransformer?.transformFunctionCallInternal(
- provideDelegateCall, ResolutionMode.ContextDependent, provideDelegate = true
+ provideDelegateCall, ResolutionMode.ReceiverResolution, provideDelegate = true
)
// If we got successful candidate for provideDelegate, let's select it
@@ -438,10 +430,19 @@
return provideDelegateCall
}
- // Select delegate expression otherwise
- return delegateExpression
+ return null
}
+ override fun transformPropertyAccessor(propertyAccessor: FirPropertyAccessor, data: ResolutionMode): FirPropertyAccessor {
+ return propertyAccessor.also {
+ transformProperty(it.propertySymbol.fir, data)
+ }
+ }
+
+ private fun transformDelegateExpression(delegate: FirWrappedDelegateExpression): FirExpression =
+ delegate.expression.transformSingle(transformer, ResolutionMode.ContextDependent.Delegate)
+ .transformSingle(components.integerLiteralAndOperatorApproximationTransformer, null)
+
/**
* For supporting the case when `provideDelegate` has a signature with type variable as a return type, like
* fun <K> K.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = this
@@ -481,11 +482,6 @@
val candidateSystem = candidate.system
val candidateStorage = candidateSystem.currentStorage()
- val allTypeVariables = candidateStorage.allTypeVariables.keys.toList()
- // Subset of type variables obtained from the `provideDelegate` call
- val typeVariablesRelatedToProvideDelegate =
- allTypeVariables.subList(candidateStorage.outerSystemVariablesPrefixSize, allTypeVariables.size).toSet()
-
val variableWithConstraints =
candidateSystem.notFixedTypeVariables[typeVariable] ?: error("Not found type variable $typeVariable")
@@ -494,7 +490,7 @@
// Temporary declare all the "outer" variables as proper (i.e., all inner variables as improper)
// Without that, all variables (both inner and outer ones) would be considered as improper,
// while we want to fix to assume `Delegate<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)
// 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>
@@ -506,7 +502,7 @@
// it seems like they do something relevant.
resultType = inferenceComponents.resultTypeResolver.findResultTypeOrNull(
candidateSystem, variableWithConstraints, TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN
- ) as? ConeKotlinType ?: return@withTypeVariablesThatAreNotCountedAsProperTypes
+ ) as? ConeKotlinType ?: return@withTypeVariablesThatAreCountedAsProperTypes
check(!candidateStorage.hasContradiction) { "We only should try fixing variables on successful provideDelegate candidate" }
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt
index 34533b8..a284fc5 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt
@@ -31,7 +31,6 @@
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
-import org.jetbrains.kotlin.fir.resolve.inference.FirStubInferenceSession
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.replaceLambdaArgumentInvocationKinds
import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperator
@@ -91,7 +90,7 @@
transformQualifiedAccessExpression(qualifiedAccessExpression, data, isUsedAsReceiver = false, isUsedAsGetClassReceiver = false)
}
- fun transformQualifiedAccessExpression(
+ private fun transformQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression,
data: ResolutionMode,
isUsedAsReceiver: Boolean,
@@ -1035,6 +1034,13 @@
),
)
+ // for cases like
+ // buildSomething { tVar = "" // Should infer TV from String assignment }
+ context.inferenceSession.addSubtypeConstraintIfCompatible(
+ variableAssignment.lValue.resolvedType, variableAssignment.rValue.resolvedType,
+ variableAssignment,
+ )
+
dataFlowAnalyzer.exitVariableAssignment(result)
return result
@@ -1053,10 +1059,10 @@
val transformedLHS = when (explicitReceiver) {
is FirPropertyAccessExpression ->
transformQualifiedAccessExpression(
- explicitReceiver, ResolutionMode.ContextIndependent, isUsedAsReceiver = true, isUsedAsGetClassReceiver = false
+ explicitReceiver, ResolutionMode.ReceiverResolution.ForCallableReference, isUsedAsReceiver = true, isUsedAsGetClassReceiver = false
) as FirExpression
else ->
- explicitReceiver?.transformSingle(this, ResolutionMode.ContextIndependent)
+ explicitReceiver?.transformSingle(this, ResolutionMode.ReceiverResolution.ForCallableReference)
}.apply {
if (this is FirResolvedQualifier && callableReferenceAccess.hasQuestionMarkAtLHS) {
replaceIsNullableLHSForCallableReference(true)
@@ -1181,7 +1187,7 @@
constExpression.replaceKind(expressionType.toConstKind() as ConstantValueKind<T>)
expressionType
}
- data is ResolutionMode.ReceiverResolution -> {
+ data is ResolutionMode.ReceiverResolution && !data.forCallableReference -> {
require(expressionType is ConeIntegerLiteralConstantTypeImpl)
ConeIntegerConstantOperatorTypeImpl(expressionType.isUnsigned, ConeNullability.NOT_NULL)
}
diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt
index e1854f3..7e4e9d3 100644
--- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt
+++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt
@@ -7,6 +7,7 @@
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.declarations.FirFunction
+import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression
@@ -15,7 +16,6 @@
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
-import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeVariable
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
@@ -158,6 +158,7 @@
object TypeParameterAsExpression : ResolutionDiagnostic(INAPPLICABLE)
-class StubBuilderInferenceReceiver(
- val typeParameterSymbol: FirTypeParameterSymbol
-) : ResolutionDiagnostic(RESOLVED_WITH_ERROR)
+class TypeVariableAsExplicitReceiver(
+ val explicitReceiver: FirExpression,
+ val typeParameter: FirTypeParameter,
+) : ResolutionDiagnostic(RESOLVED_WITH_ERROR)
\ No newline at end of file
diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt
index a0182e0..a035cc4 100644
--- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt
+++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt
@@ -596,6 +596,11 @@
return IrTypeSubstitutor(emptyMap())
}
+ @K2Only
+ override fun createSubstitutionFromSubtypingStubTypesToTypeVariables(): TypeSubstitutorMarker {
+ error("Only for inference")
+ }
+
override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker {
require(this is AbstractIrTypeSubstitutor)
require(type is IrType)
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzerContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzerContext.kt
index 68cd259..d54f073 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzerContext.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzerContext.kt
@@ -7,10 +7,13 @@
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
+import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
import org.jetbrains.kotlin.types.model.*
interface PostponedArgumentsAnalyzerContext : TypeSystemInferenceExtensionContext {
- fun buildCurrentSubstitutor(additionalBindings: Map<TypeConstructorMarker, StubTypeMarker>): TypeSubstitutorMarker
+ val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>
+
+ fun buildCurrentSubstitutor(additionalBindings: Map<TypeConstructorMarker, KotlinTypeMarker>): TypeSubstitutorMarker
fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker
fun bindingStubsForPostponedVariables(): Map<TypeVariableMarker, StubTypeMarker>
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt
index 8276138..9adfaff 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt
@@ -11,21 +11,16 @@
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
+import org.jetbrains.kotlin.types.model.K2Only
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
import org.jetbrains.kotlin.types.model.TypeVariableMarker
abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Context, ResultTypeResolver.Context {
- abstract val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker>
abstract override val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>
abstract override val fixedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker>
abstract override val postponedTypeVariables: List<TypeVariableMarker>
- /**
- * See [org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.outerSystemVariablesPrefixSize]
- */
- abstract val outerSystemVariablesPrefixSize: Int
-
abstract fun getBuilder(): ConstraintSystemBuilder
// type can be proper if it not contains not fixed type variables
@@ -69,7 +64,7 @@
completionMode: ConstraintSystemCompletionMode,
analyze: (A) -> Unit
): Boolean {
- if (completionMode == ConstraintSystemCompletionMode.FULL) {
+ if (completionMode.allLambdasShouldBeAnalyzed) {
val argumentWithTypeVariableAsExpectedType = findPostponedArgumentWithRevisableExpectedType(postponedArguments)
if (argumentWithTypeVariableAsExpectedType != null) {
@@ -120,4 +115,11 @@
!it.typeConstructor().isClassTypeConstructor() && !it.typeConstructor().isTypeParameterTypeConstructor()
}
}.map { it.type }
+
+ /**
+ * @see [org.jetbrains.kotlin.resolve.calls.inference.components.VariableFixationFinder.Context.typeVariablesThatAreNotCountedAsProperTypes]
+ * @see [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded]
+ */
+ @K2Only
+ abstract fun <R> withTypeVariablesThatAreCountedAsProperTypes(typeVariables: Set<TypeConstructorMarker>, block: () -> R): R
}
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionMode.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionMode.kt
index 679c171..62dea37 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionMode.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionMode.kt
@@ -5,8 +5,9 @@
package org.jetbrains.kotlin.resolve.calls.inference.components
-enum class ConstraintSystemCompletionMode {
- FULL,
+enum class ConstraintSystemCompletionMode(val allLambdasShouldBeAnalyzed: Boolean) {
+ FULL(true),
+ PCLA_POSTPONED_CALL(true),
/**
* This mode allows us to infer variables in calls, which have enough type-info to be completed right-away
@@ -18,6 +19,6 @@
* x.plus(run { x }) // Here, to select plus overload we need to analyze lambda
* ```
*/
- PARTIAL,
- UNTIL_FIRST_LAMBDA
+ PARTIAL(false),
+ UNTIL_FIRST_LAMBDA(false),
}
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt
index 79d1edb..08219b5 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt
@@ -9,8 +9,13 @@
import org.jetbrains.kotlin.builtins.functions.isBasicFunctionOrKFunction
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
-import org.jetbrains.kotlin.resolve.calls.inference.model.*
-import org.jetbrains.kotlin.resolve.calls.model.*
+import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
+import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
+import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
+import org.jetbrains.kotlin.resolve.calls.model.LambdaWithTypeVariableAsExpectedTypeMarker
+import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType
+import org.jetbrains.kotlin.resolve.calls.model.PostponedCallableReferenceMarker
+import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
import org.jetbrains.kotlin.types.model.*
import org.jetbrains.kotlin.utils.SmartSet
import java.util.*
@@ -531,24 +536,39 @@
postponedArguments: List<PostponedResolvedAtomMarker>,
topLevelType: KotlinTypeMarker,
dependencyProvider: TypeVariableDependencyInformationProvider,
- resolvedAtomProvider: ResolvedAtomProvider
- ): Boolean = with(c) {
- val expectedType = argument.run { (this as? PostponedAtomWithRevisableExpectedType)?.revisedExpectedType ?: expectedType }
+ resolvedAtomProvider: ResolvedAtomProvider,
+ ): Boolean {
+ val expectedType = argument.expectedFunctionType(c) ?: return false
- if (expectedType != null && expectedType.isFunctionOrKFunctionWithAnySuspendability()) {
- val wasFixedSomeVariable = c.fixNextReadyVariableForParameterType(
- expectedType,
- postponedArguments,
- topLevelType,
- dependencyProvider,
- resolvedAtomProvider
- )
+ return c.fixNextReadyVariableForParameterType(
+ expectedType,
+ postponedArguments,
+ topLevelType,
+ dependencyProvider,
+ resolvedAtomProvider,
+ )
+ }
- if (wasFixedSomeVariable)
- return true
- }
+ 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(
@@ -558,17 +578,9 @@
dependencyProvider: TypeVariableDependencyInformationProvider,
resolvedAtomByTypeVariableProvider: ResolvedAtomProvider,
): Boolean = with(resolutionTypeSystemContext) {
- val relatedVariables = type.extractArgumentsForFunctionTypeOrSubtype()
- .flatMap { getAllDeeplyRelatedTypeVariables(it, dependencyProvider) }
- val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
- this@fixNextReadyVariableForParameterType,
- relatedVariables,
- postponedArguments,
- ConstraintSystemCompletionMode.FULL,
- topLevelType
- )
+ val variableForFixation = findNextVariableForParameterType(type, dependencyProvider, postponedArguments, topLevelType)
- if (variableForFixation == null || !variableForFixation.hasProperConstraint)
+ if (variableForFixation == null || !variableForFixation.isReady)
return false
val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable)
@@ -589,6 +601,26 @@
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) }
companion object {
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt
index de12ac5..ebef7dc 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt
@@ -22,6 +22,8 @@
private val languageVersionSettings: LanguageVersionSettings
) {
interface Context : TypeSystemInferenceExtensionContext {
+ val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>
+ val outerSystemVariablesPrefixSize: Int
fun isProperType(type: KotlinTypeMarker): Boolean
fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker
fun isReified(variable: TypeVariableMarker): Boolean
@@ -220,6 +222,10 @@
if (typesWithoutStubs.isNotEmpty()) {
commonSuperType = computeCommonSuperType(typesWithoutStubs)
+ } else if (outerSystemVariablesPrefixSize > 0) {
+ // outerSystemVariablesPrefixSize > 0 only for PCLA (K2)
+ @OptIn(K2Only::class)
+ commonSuperType = createSubstitutionFromSubtypingStubTypesToTypeVariables().safeSubstitute(commonSuperType)
}
}
@@ -273,6 +279,14 @@
}
if (!atLeastOneProper) return emptyList()
+
+ // PCLA slow path
+ // We only allow using TVs fixation for nested PCLA calls
+ if (outerSystemVariablesPrefixSize > 0) {
+ val notFixedToStubTypesSubstitutor = buildNotFixedVariablesToStubTypesSubstitutor()
+ return lowerConstraintTypes.map { notFixedToStubTypesSubstitutor.safeSubstitute(it) }
+ }
+
if (!atLeastOneNonProper) return lowerConstraintTypes
val notFixedToStubTypesSubstitutor = buildNotFixedVariablesToStubTypesSubstitutor()
@@ -336,7 +350,7 @@
}
private fun Context.isProperTypeForFixation(type: KotlinTypeMarker): Boolean =
- isProperTypeForFixation(type) { isProperType(it) }
+ isProperTypeForFixation(type, notFixedTypeVariables.keys) { isProperType(it) }
private fun findResultIfThereIsEqualsConstraint(c: Context, variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? =
with(c) {
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt
index 7b7ae72..0d502eb 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt
@@ -7,15 +7,22 @@
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
-import org.jetbrains.kotlin.types.model.*
+import org.jetbrains.kotlin.types.model.KotlinTypeMarker
+import org.jetbrains.kotlin.types.model.TypeConstructorMarker
+import org.jetbrains.kotlin.types.model.freshTypeConstructor
+import org.jetbrains.kotlin.types.model.typeConstructor
import org.jetbrains.kotlin.utils.SmartSet
class TypeVariableDependencyInformationProvider(
private val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>,
private val postponedKtPrimitives: List<PostponedResolvedAtomMarker>,
private val topLevelType: KotlinTypeMarker?,
- private val typeSystemContext: TypeSystemInferenceExtensionContext
+ private val typeSystemContext: VariableFixationFinder.Context
) {
+
+ private val outerTypeVariables: Set<TypeConstructorMarker>? =
+ typeSystemContext.outerTypeVariables
+
/*
* Not oriented edges
* TypeVariable(A) has UPPER(Function1<TypeVariable(B), R>) => A and B are related deeply
@@ -41,7 +48,16 @@
computeRelatedToTopLevelType()
}
- fun isVariableRelatedToTopLevelType(variable: TypeConstructorMarker) = relatedToTopLevelType.contains(variable)
+ fun isVariableRelatedToTopLevelType(variable: TypeConstructorMarker) =
+ relatedToTopLevelType.contains(variable)
+
+
+ fun isRelatedToOuterTypeVariable(variable: TypeConstructorMarker): Boolean {
+ val outerTypeVariables = outerTypeVariables ?: return false
+ val myDependent = getDeeplyDependentVariables(variable) ?: return false
+ return myDependent.any { it in outerTypeVariables }
+ }
+
fun isVariableRelatedToAnyOutputType(variable: TypeConstructorMarker) = relatedToAllOutputTypes.contains(variable)
fun getDeeplyDependentVariables(variable: TypeConstructorMarker) = deepTypeVariableDependencies[variable]
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt
index 8eb18a9..24cb73c 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt
@@ -27,25 +27,40 @@
val fixedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker>
val postponedTypeVariables: List<TypeVariableMarker>
val constraintsFromAllForkPoints: MutableList<Pair<IncorporationConstraintPosition, ForkPointData>>
+ val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker>
/**
- * If not null, that property means that we should assume temporary
- * `allTypeVariables.keys.minus(typeVariablesThatAreNotCountedAsProperTypes)` as proper types when fixating some variables.
+ * See [org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.outerSystemVariablesPrefixSize]
+ */
+ val outerSystemVariablesPrefixSize: Int
+
+ val outerTypeVariables: Set<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.
*
* Currently, that is only used for `provideDelegate` resolution, see
* [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded]
*/
- val typeVariablesThatAreNotCountedAsProperTypes: Set<TypeConstructorMarker>?
+ val typeVariablesThatAreCountedAsProperTypes: Set<TypeConstructorMarker>?
fun isReified(variable: TypeVariableMarker): Boolean
}
- data class VariableForFixation(
+ class VariableForFixation(
val variable: TypeConstructorMarker,
- val hasProperConstraint: Boolean,
- )
+ private val hasProperConstraint: Boolean,
+ private val hasDependencyOnOuterTypeVariable: Boolean = false,
+ ) {
+ val isReady: Boolean get() = hasProperConstraint && !hasDependencyOnOuterTypeVariable
+ }
fun findFirstVariableForFixation(
c: Context,
@@ -53,11 +68,13 @@
postponedKtPrimitives: List<PostponedResolvedAtomMarker>,
completionMode: ConstraintSystemCompletionMode,
topLevelType: KotlinTypeMarker,
- ): VariableForFixation? = c.findTypeVariableForFixation(allTypeVariables, postponedKtPrimitives, completionMode, topLevelType)
+ ): VariableForFixation? =
+ c.findTypeVariableForFixation(allTypeVariables, postponedKtPrimitives, completionMode, topLevelType)
enum class TypeVariableFixationReadiness {
FORBIDDEN,
WITHOUT_PROPER_ARGUMENT_CONSTRAINT, // proper constraint from arguments -- not from upper bound for type parameters
+ OUTER_TYPE_VARIABLE_DEPENDENCY,
READY_FOR_FIXATION_DECLARED_UPPER_BOUND_WITH_SELF_TYPES,
WITH_COMPLEX_DEPENDENCY, // if type variable T has constraint with non fixed type variable inside (non-top-level): T <: Foo<S>
ALL_CONSTRAINTS_TRIVIAL_OR_NON_PROPER, // proper trivial constraint from arguments, Nothing <: T
@@ -85,6 +102,7 @@
isTypeInferenceForSelfTypesSupported && areAllProperConstraintsSelfTypeBased(variable) ->
TypeVariableFixationReadiness.READY_FOR_FIXATION_DECLARED_UPPER_BOUND_WITH_SELF_TYPES
!variableHasProperArgumentConstraints(variable) -> TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT
+ dependencyProvider.isRelatedToOuterTypeVariable(variable) -> TypeVariableFixationReadiness.OUTER_TYPE_VARIABLE_DEPENDENCY
hasDependencyToOtherTypeVariables(variable) -> TypeVariableFixationReadiness.WITH_COMPLEX_DEPENDENCY
// TODO: Consider removing this kind of readiness, see KT-63032
allConstraintsTrivialOrNonProper(variable) -> TypeVariableFixationReadiness.ALL_CONSTRAINTS_TRIVIAL_OR_NON_PROPER
@@ -152,7 +170,7 @@
if (allTypeVariables.isEmpty()) return null
val dependencyProvider = TypeVariableDependencyInformationProvider(
- notFixedTypeVariables, postponedArguments, topLevelType.takeIf { completionMode == PARTIAL }, this
+ notFixedTypeVariables, postponedArguments, topLevelType.takeIf { completionMode == PARTIAL }, this,
)
val candidate =
@@ -161,6 +179,8 @@
return when (getTypeVariableReadiness(candidate, dependencyProvider)) {
TypeVariableFixationReadiness.FORBIDDEN -> null
TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT -> VariableForFixation(candidate, false)
+ TypeVariableFixationReadiness.OUTER_TYPE_VARIABLE_DEPENDENCY ->
+ VariableForFixation(candidate, hasProperConstraint = true, hasDependencyOnOuterTypeVariable = true)
else -> VariableForFixation(candidate, true)
}
@@ -190,12 +210,13 @@
&& !c.isNullabilityConstraint
private fun Context.isProperType(type: KotlinTypeMarker): Boolean =
- isProperTypeForFixation(type) { t -> !t.contains { isNotFixedRelevantVariable(it) } }
+ isProperTypeForFixation(type, notFixedTypeVariables.keys) { t -> !t.contains { isNotFixedRelevantVariable(it) } }
private fun Context.isNotFixedRelevantVariable(it: KotlinTypeMarker): Boolean {
- if (!notFixedTypeVariables.containsKey(it.typeConstructor())) return false
- if (typeVariablesThatAreNotCountedAsProperTypes == null) return true
- return typeVariablesThatAreNotCountedAsProperTypes!!.contains(it.typeConstructor())
+ val key = it.typeConstructor()
+ if (!notFixedTypeVariables.containsKey(key)) return false
+ if (typeVariablesThatAreCountedAsProperTypes?.contains(key) == true) return false
+ return true
}
private fun Context.isReified(variable: TypeConstructorMarker): Boolean =
@@ -235,8 +256,25 @@
}
}
-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> {
if (baseType.isFlexible()) {
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt
index 9f8fb0f..40d87ec 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt
@@ -49,6 +49,8 @@
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
* of [allTypeVariables] belong to the outer CS.
*
@@ -62,6 +64,8 @@
*/
val outerSystemVariablesPrefixSize: Int
+ val usesOuterCs: Boolean
+
object Empty : ConstraintStorage {
override val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker> get() = emptyMap()
override val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints> get() = emptyMap()
@@ -77,6 +81,8 @@
override val constraintsFromAllForkPoints: List<Pair<IncorporationConstraintPosition, ForkPointData>> = emptyList()
override val outerSystemVariablesPrefixSize: Int get() = 0
+
+ override val usesOuterCs: Boolean get() = false
}
}
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt
index 700ad4e..9f66363 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt
@@ -221,6 +221,14 @@
}
}
+ fun runConstraintsSimplification() {
+ val currentState = constraints.toList()
+ mutableConstraints.apply {
+ clear()
+ addAll(currentState)
+ }
+ }
+
private fun isUsefulConstraint(constraint: Constraint, equalityConstraints: Map<Int, List<Constraint>>): Boolean {
if (constraint.kind == ConstraintKind.EQUALITY) return true
return equalityConstraints[constraint.typeHashCode]?.none { it.type == constraint.type } ?: true
@@ -251,4 +259,12 @@
override val constraintsFromAllForkPoints: MutableList<Pair<IncorporationConstraintPosition, ForkPointData>> = SmartList()
override var outerSystemVariablesPrefixSize: Int = 0
+
+ override var usesOuterCs: Boolean = false
+
+ @AssertionsOnly
+ internal var outerCS: ConstraintStorage? = null
}
+
+@RequiresOptIn
+annotation class AssertionsOnly
\ No newline at end of file
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt
index d14628c..dbbc6da 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt
@@ -41,7 +41,7 @@
private val properTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
private val notProperTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
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
@@ -51,23 +51,26 @@
* @see [org.jetbrains.kotlin.resolve.calls.inference.components.VariableFixationFinder.Context.typeVariablesThatAreNotCountedAsProperTypes]
* @see [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded]
*/
- fun withTypeVariablesThatAreNotCountedAsProperTypes(typeVariables: Set<TypeConstructorMarker>, block: () -> Unit) {
+ @K2Only
+ override fun <R> withTypeVariablesThatAreCountedAsProperTypes(typeVariables: Set<TypeConstructorMarker>, block: () -> R): R {
checkState(State.BUILDING)
// Cleaning cache is necessary because temporarily we change the meaning of what does "proper type" mean
properTypesCache.clear()
notProperTypesCache.clear()
- require(typeVariablesThatAreNotCountedAsProperTypes == null) {
+ require(typeVariablesThatAreCountedAsProperTypes == null) {
"Currently there should be no nested withDisallowingOnlyThisTypeVariablesForProperTypes calls"
}
- typeVariablesThatAreNotCountedAsProperTypes = typeVariables
+ typeVariablesThatAreCountedAsProperTypes = typeVariables
- block()
+ val result = block()
- typeVariablesThatAreNotCountedAsProperTypes = null
+ typeVariablesThatAreCountedAsProperTypes = null
properTypesCache.clear()
notProperTypesCache.clear()
+
+ return result
}
private enum class State {
@@ -221,6 +224,7 @@
// ConstraintSystemBuilder
private fun transactionRegisterVariable(variable: TypeVariableMarker) {
if (state != State.TRANSACTION) return
+ if (variable.freshTypeConstructor() in storage.allTypeVariables) return
typeVariablesTransaction.add(variable)
}
@@ -302,13 +306,25 @@
}
fun addOuterSystem(outerSystem: ConstraintStorage) {
- addOtherSystem(outerSystem)
+ require(!storage.usesOuterCs)
+
+ storage.usesOuterCs = true
storage.outerSystemVariablesPrefixSize = outerSystem.allTypeVariables.size
+ @OptIn(AssertionsOnly::class)
+ storage.outerCS = outerSystem
+
+ addOtherSystem(outerSystem, isAddingOuter = true)
}
- fun setBaseSystem(outerSystem: ConstraintStorage) {
- addOtherSystem(outerSystem)
- storage.outerSystemVariablesPrefixSize = outerSystem.outerSystemVariablesPrefixSize
+ @K2Only
+ fun setBaseSystem(baseSystem: ConstraintStorage) {
+ require(storage.allTypeVariables.isEmpty())
+ storage.usesOuterCs = baseSystem.usesOuterCs
+ storage.outerSystemVariablesPrefixSize = baseSystem.outerSystemVariablesPrefixSize
+ @OptIn(AssertionsOnly::class)
+ storage.outerCS = (baseSystem as? MutableConstraintStorage)?.outerCS
+
+ addOtherSystem(baseSystem)
}
fun prepareForGlobalCompletion() {
@@ -317,6 +333,16 @@
}
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) {
+ runOuterCSRelatedAssertions(otherSystem, isAddingOuter)
+
if (otherSystem.allTypeVariables.isNotEmpty()) {
otherSystem.allTypeVariables.forEach {
transactionRegisterVariable(it.value)
@@ -324,16 +350,47 @@
storage.allTypeVariables.putAll(otherSystem.allTypeVariables)
notProperTypesCache.clear()
}
+
+ // `clearNotFixedTypeVariables` means that we're mostly replacing the content, thus we need to remove variables that have been fixed
+ // in `otherSystem` from `this.notFixedTypeVariables`, too
+ if (clearNotFixedTypeVariables) {
+ notFixedTypeVariables.clear()
+ }
+
for ((variable, constraints) in otherSystem.notFixedTypeVariables) {
notFixedTypeVariables[variable] = MutableVariableWithConstraints(this, constraints)
}
- storage.initialConstraints.addAll(otherSystem.initialConstraints)
+
+ val currentInitialConstraints = storage.initialConstraints.toSet()
+
+ otherSystem.initialConstraints.filterTo(storage.initialConstraints) {
+ it !in currentInitialConstraints
+ }
+
storage.maxTypeDepthFromInitialConstraints =
max(storage.maxTypeDepthFromInitialConstraints, otherSystem.maxTypeDepthFromInitialConstraints)
storage.errors.addAll(otherSystem.errors)
storage.fixedTypeVariables.putAll(otherSystem.fixedTypeVariables)
storage.postponedTypeVariables.addAll(otherSystem.postponedTypeVariables)
storage.constraintsFromAllForkPoints.addAll(otherSystem.constraintsFromAllForkPoints)
+
+ }
+
+
+ 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`
+ @OptIn(AssertionsOnly::class)
+ if ((otherSystem as? MutableConstraintStorage)?.outerCS === storage) return
+
+ require(storage.usesOuterCs)
+
+ if (!isAddingOuter) {
+ require(storage.outerSystemVariablesPrefixSize == otherSystem.outerSystemVariablesPrefixSize) {
+ "Expected to be ${otherSystem.outerSystemVariablesPrefixSize}, but ${storage.outerSystemVariablesPrefixSize} found"
+ }
+ }
}
// ResultTypeResolver.Context, ConstraintSystemBuilder
@@ -357,11 +414,11 @@
it
if (typeToCheck == null) return@contains false
- if (typeVariablesThatAreNotCountedAsProperTypes != null) {
- return@contains typeVariablesThatAreNotCountedAsProperTypes!!.contains(typeToCheck.typeConstructor())
+ if (typeVariablesThatAreCountedAsProperTypes?.contains(typeToCheck.typeConstructor()) == true) {
+ return@contains false
}
- storage.allTypeVariables.containsKey(typeToCheck.typeConstructor())
+ return@contains storage.allTypeVariables.containsKey(typeToCheck.typeConstructor())
}
override fun isTypeVariable(type: KotlinTypeMarker): Boolean {
@@ -530,6 +587,13 @@
otherVariableWithConstraints.removeConstrains { containsTypeVariable(it.type, freshTypeConstructor) }
}
+ val substitutorForFixedVariables = typeSubstitutorByTypeConstructor(mapOf(freshTypeConstructor to resultType))
+
+ storage.fixedTypeVariables.filter { containsTypeVariable(it.value, freshTypeConstructor) }
+ .forEach { (otherVariable, otherResultType) ->
+ storage.fixedTypeVariables[otherVariable] = substitutorForFixedVariables.safeSubstitute(otherResultType)
+ }
+
storage.fixedTypeVariables[freshTypeConstructor] = resultType
// Substitute freshly fixed type variable into missed constraints
@@ -689,7 +753,7 @@
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)
return storage.buildCurrentSubstitutor(this, additionalBindings)
}
@@ -715,6 +779,8 @@
return storage
}
+ val usesOuterCs: Boolean get() = storage.usesOuterCs
+
// PostponedArgumentsAnalyzer.Context
override fun hasUpperOrEqualUnitConstraint(type: KotlinTypeMarker): Boolean {
checkState(State.BUILDING, State.COMPLETION, State.FREEZED)
diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt
index 45740d7..6f2688b 100644
--- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt
+++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt
@@ -10,22 +10,25 @@
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor
-import org.jetbrains.kotlin.resolve.calls.components.candidate.ResolutionCandidate
import org.jetbrains.kotlin.resolve.calls.components.candidate.CallableReferenceResolutionCandidate
+import org.jetbrains.kotlin.resolve.calls.components.candidate.ResolutionCandidate
import org.jetbrains.kotlin.resolve.calls.components.candidate.SimpleResolutionCandidate
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.addEqualityConstraintIfCompatible
-import org.jetbrains.kotlin.resolve.calls.inference.components.*
+import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
+import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter
+import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutorByConstructorMap
+import org.jetbrains.kotlin.resolve.calls.inference.components.TrivialConstraintTypeInferenceOracle
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.Empty.hasContradiction
import org.jetbrains.kotlin.resolve.calls.inference.model.ExpectedTypeConstraintPositionImpl
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.tower.CandidateFactory
import org.jetbrains.kotlin.resolve.calls.tower.forceResolution
-import org.jetbrains.kotlin.types.error.ErrorUtils
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.error.ErrorType
import org.jetbrains.kotlin.types.error.ErrorTypeKind
+import org.jetbrains.kotlin.types.error.ErrorUtils
import org.jetbrains.kotlin.types.model.safeSubstitute
import org.jetbrains.kotlin.utils.addToStdlib.same
@@ -80,6 +83,7 @@
candidate.runCompletion(completionMode, diagnosticHolder, resolutionCallbacks)
candidate.asCallResolutionResult(completionMode, diagnosticHolder)
}
+ ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL -> error("PCLA might be only run for K2")
ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA -> throw IllegalStateException("Should not be here")
}
diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt
index 9585b46..f5dbfd2 100644
--- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt
+++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt
@@ -12,7 +12,10 @@
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.model.MultiLambdaBuilderInferenceRestriction
-import org.jetbrains.kotlin.types.*
+import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.types.KotlinTypeFactory
+import org.jetbrains.kotlin.types.TypeConstructor
+import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.error.ErrorTypeKind
import org.jetbrains.kotlin.types.error.ErrorUtils
import org.jetbrains.kotlin.types.model.*
@@ -279,7 +282,7 @@
// continue completion (rerun stages) only if ready for fixation variables with proper constraints have appeared
// (after analysing a lambda with the builder inference)
// otherwise we don't continue and report "not enough type information" error
- return variableForFixation?.hasProperConstraint == true
+ return variableForFixation?.isReady == true
}
private fun transformToAtomWithNewFunctionalExpectedType(
@@ -319,7 +322,7 @@
topLevelType
) ?: return false
- if (!variableForFixation.hasProperConstraint) return false
+ if (!variableForFixation.isReady) return false
fixVariable(this, notFixedTypeVariables.getValue(variableForFixation.variable), topLevelAtoms, diagnosticsHolder)
@@ -340,7 +343,7 @@
postponedArguments, completionMode, topLevelType,
) ?: break
- assert(!variableForFixation.hasProperConstraint) {
+ assert(!variableForFixation.isReady) {
"At this stage there should be no remaining variables with proper constraints"
}
diff --git a/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt b/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt
index 7d69e04..4ed5080 100644
--- a/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt
+++ b/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt
@@ -8,7 +8,9 @@
import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind
import org.jetbrains.kotlin.resolve.checkers.EmptyIntersectionTypeChecker
import org.jetbrains.kotlin.resolve.checkers.EmptyIntersectionTypeInfo
-import org.jetbrains.kotlin.types.*
+import org.jetbrains.kotlin.types.AbstractTypeChecker
+import org.jetbrains.kotlin.types.TypeCheckerState
+import org.jetbrains.kotlin.types.Variance
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@@ -551,6 +553,9 @@
fun typeSubstitutorByTypeConstructor(map: Map<TypeConstructorMarker, KotlinTypeMarker>): TypeSubstitutorMarker
fun createEmptySubstitutor(): TypeSubstitutorMarker
+ @K2Only
+ fun createSubstitutionFromSubtypingStubTypesToTypeVariables(): TypeSubstitutorMarker
+
/**
* @returns substituted type or [type] if there were no substitution
*/
@@ -588,3 +593,6 @@
@RequiresOptIn("This kinds of type is obsolete and should not be used until you really need it")
annotation class ObsoleteTypeKind
+
+@RequiresOptIn
+annotation class K2Only
diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt
index 569c1a9..e755500 100644
--- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt
+++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt
@@ -622,6 +622,11 @@
errorSupportedOnlyInTypeInference()
}
+ @K2Only
+ override fun createSubstitutionFromSubtypingStubTypesToTypeVariables(): TypeSubstitutorMarker {
+ error("Only for K2")
+ }
+
override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker {
require(type is UnwrappedType, type::errorMessage)
require(this is TypeSubstitutor, this::errorMessage)