~ initial
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/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt
index 0748e0e..0af6bf5 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
@@ -39,6 +39,7 @@
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
@@ -183,6 +184,11 @@
is ConeAmbiguousFunctionTypeKinds -> FirErrors.AMBIGUOUS_FUNCTION_TYPE_KIND.createOn(source, kinds)
is ConeUnsupportedClassLiteralsWithEmptyLhs -> FirErrors.UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS.createOn(source)
is ConeMissingConstructorKeyword -> FirErrors.MISSING_CONSTRUCTOR_KEYWORD.createOn(source)
+ is ConeUsingTypeVariableAsExplicitReceiver -> FirErrors.BUILDER_INFERENCE_STUB_RECEIVER.createOn(
+ explicitReceiver.source,
+ typeParameter.symbol.name,
+ (typeParameter.symbol.containingDeclarationSymbol as? FirCallableSymbol<*>)?.name ?: Name.identifier("unknown")
+ )
else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}")
}
@@ -412,6 +418,7 @@
)
}
}.ifEmpty {
+
listOfNotNull(
diagnostic.candidate.errors.firstNotNullOfOrNull {
val message = when (it) {
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/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 b5bd9c0..df0a0fe 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,9 @@
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.FirPCLAInferenceSession
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
@@ -74,13 +75,19 @@
val conflictResolver: ConeCallConflictResolver =
session.callConflictResolverFactory.create(TypeSpecificityComparator.NONE, session.inferenceComponents, components)
- fun resolveCallAndSelectCandidate(functionCall: FirFunctionCall): FirFunctionCall {
+ fun resolveCallAndSelectCandidate(functionCall: FirFunctionCall, resolutionMode: ResolutionMode): FirFunctionCall {
val name = functionCall.calleeReference.name
- val result = collectCandidates(functionCall, name, origin = functionCall.origin)
+ val result = collectCandidates(functionCall, name, origin = functionCall.origin, resolutionMode = resolutionMode)
var forceCandidates: Collection<Candidate>? = null
if (result.candidates.isEmpty()) {
- val newResult = collectCandidates(functionCall, name, CallKind.VariableAccess, origin = functionCall.origin)
+ val newResult = collectCandidates(
+ functionCall,
+ name,
+ CallKind.VariableAccess,
+ origin = functionCall.origin,
+ resolutionMode = resolutionMode
+ )
if (newResult.candidates.isNotEmpty()) {
forceCandidates = newResult.candidates
}
@@ -139,14 +146,22 @@
qualifiedAccess: FirQualifiedAccessExpression,
name: Name,
containingDeclarations: List<FirDeclaration> = transformer.components.containingDeclarations,
- resolutionContext: ResolutionContext = transformer.resolutionContext
+ resolutionContext: ResolutionContext = transformer.resolutionContext,
+ resolutionMode: ResolutionMode,
): List<OverloadCandidate> {
val collector = AllCandidatesCollector(components, components.resolutionStageRunner)
val origin = (qualifiedAccess as? FirFunctionCall)?.origin ?: FirFunctionCallOrigin.Regular
- val result = collectCandidates(
- qualifiedAccess, name, forceCallKind = null, isUsedAsGetClassReceiver = false,
- origin, containingDeclarations, resolutionContext, collector
- )
+ val result =
+ collectCandidates(
+ qualifiedAccess,
+ name,
+ forceCallKind = null,
+ isUsedAsGetClassReceiver = false, origin,
+ containingDeclarations,
+ resolutionContext,
+ collector,
+ resolutionMode = resolutionMode
+ )
return collector.allCandidates.map { OverloadCandidate(it, isInBestCandidates = it in result.candidates) }
}
@@ -160,6 +175,7 @@
resolutionContext: ResolutionContext = transformer.resolutionContext,
collector: CandidateCollector? = null,
callSite: FirElement = qualifiedAccess,
+ resolutionMode: ResolutionMode,
): ResolutionResult {
val explicitReceiver = qualifiedAccess.explicitReceiver
val argumentList = (qualifiedAccess as? FirFunctionCall)?.argumentList ?: FirEmptyArgumentList
@@ -177,7 +193,8 @@
session,
components.file,
containingDeclarations,
- origin = origin
+ origin = origin,
+ resolutionMode = resolutionMode,
)
towerResolver.reset()
val result = towerResolver.runResolver(info, resolutionContext, collector)
@@ -228,15 +245,23 @@
isUsedAsReceiver: Boolean,
isUsedAsGetClassReceiver: Boolean,
callSite: FirElement,
+ resolutionMode: ResolutionMode,
): FirStatement {
- return resolveVariableAccessAndSelectCandidateImpl(qualifiedAccess, isUsedAsReceiver, isUsedAsGetClassReceiver, callSite) { true }
+ return resolveVariableAccessAndSelectCandidateImpl(
+ qualifiedAccess,
+ isUsedAsReceiver,
+ resolutionMode,
+ isUsedAsGetClassReceiver,
+ callSite
+ ) { true }
}
private fun resolveVariableAccessAndSelectCandidateImpl(
qualifiedAccess: FirQualifiedAccessExpression,
isUsedAsReceiver: Boolean,
+ resolutionMode: ResolutionMode,
isUsedAsGetClassReceiver: Boolean,
- callSite: FirElement,
+ callSite: FirElement = qualifiedAccess,
acceptCandidates: (Collection<Candidate>) -> Boolean,
): FirStatement {
val callee = qualifiedAccess.calleeReference as? FirSimpleNamedReference ?: return qualifiedAccess
@@ -246,7 +271,7 @@
val nonFatalDiagnosticFromExpression = (qualifiedAccess as? FirPropertyAccessExpression)?.nonFatalDiagnostics
val basicResult by lazy(LazyThreadSafetyMode.NONE) {
- collectCandidates(qualifiedAccess, callee.name, isUsedAsGetClassReceiver = isUsedAsGetClassReceiver, callSite = callSite)
+ collectCandidates(qualifiedAccess, callee.name, isUsedAsGetClassReceiver = isUsedAsGetClassReceiver, callSite = callSite, resolutionMode = resolutionMode)
}
// Even if it's not receiver, it makes sense to continue qualifier if resolution is unsuccessful
@@ -286,7 +311,7 @@
var functionCallExpected = false
if (result.candidates.isEmpty() && qualifiedAccess !is FirFunctionCall) {
- val newResult = collectCandidates(qualifiedAccess, callee.name, CallKind.Function)
+ val newResult = collectCandidates(qualifiedAccess, callee.name, CallKind.Function, resolutionMode = resolutionMode)
if (newResult.candidates.isNotEmpty()) {
result = newResult
functionCallExpected = true
@@ -373,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
@@ -425,7 +451,7 @@
calleeReference.source
)
resolvedCallableReferenceAtom.resultingReference = errorReference
- return applicability to false
+ return@runCallableReferenceResolution applicability to false
}
reducedCandidates.size > 1 -> {
if (resolvedCallableReferenceAtom.hasBeenPostponed) {
@@ -435,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
}
}
@@ -461,7 +487,7 @@
resolvedCallableReferenceAtom.resultingReference = reference
resolvedCallableReferenceAtom.resultingTypeForCallableReference = chosenCandidate.resultingTypeForCallableReference
- return applicability to true
+ return@runCallableReferenceResolution applicability to true
}
fun resolveDelegatingConstructorCall(
@@ -488,6 +514,7 @@
session,
components.file,
components.containingDeclarations,
+ resolutionMode = ResolutionMode.ContextIndependent,
)
towerResolver.reset()
@@ -623,7 +650,8 @@
typeArguments = annotation.typeArguments,
session,
components.file,
- components.containingDeclarations
+ components.containingDeclarations,
+ resolutionMode = ResolutionMode.ContextIndependent,
)
private fun getConstructorSymbol(annotationClassSymbol: FirRegularClassSymbol): FirConstructorSymbol? {
@@ -700,6 +728,7 @@
components.file,
transformer.components.containingDeclarations,
candidateForCommonInvokeReceiver = null,
+ resolutionMode = ResolutionMode.ContextIndependent,
// Additional things for callable reference resolve
expectedType,
outerConstraintSystemBuilder,
@@ -831,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 (components.context.inferenceSession !is FirPCLAInferenceSession &&
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..633d6f5 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,6 @@
firstCandidate.system,
firstAtom,
firstCandidate,
- ConstraintSystemCompletionMode.FULL,
)
while (iterator.hasNext()) {
val (candidate, atom) = iterator.next()
@@ -147,7 +126,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..0736f12 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,26 +7,41 @@
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 inferenceSessionForAnonymousFunctions: MutableMap<FirAnonymousFunctionSymbol, FirInferenceSession> = mutableMapOf()
private val towerDataContextForCallableReferences: MutableMap<FirCallableReferenceAccess, FirTowerDataContext> = mutableMapOf()
fun getAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol): FirTowerDataContext? {
return towerDataContextForAnonymousFunctions[symbol]
}
+ fun getAnonymousFunctionInferenceSession(symbol: FirAnonymousFunctionSymbol): FirInferenceSession? {
+ return inferenceSessionForAnonymousFunctions[symbol]
+ }
+
fun getCallableReferenceContext(access: FirCallableReferenceAccess): FirTowerDataContext? {
return towerDataContextForCallableReferences[access]
}
- fun storeAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol, context: FirTowerDataContext) {
+ fun storeAnonymousFunctionContext(
+ symbol: FirAnonymousFunctionSymbol,
+ context: FirTowerDataContext,
+ inferenceSession: FirInferenceSession?,
+ ) {
towerDataContextForAnonymousFunctions[symbol] = context
+
+ if (inferenceSession != null) {
+ inferenceSessionForAnonymousFunctions[symbol] = inferenceSession
+ }
}
fun dropAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol) {
towerDataContextForAnonymousFunctions.remove(symbol)
+ inferenceSessionForAnonymousFunctions.remove(symbol)
}
fun storeCallableReferenceContext(access: FirCallableReferenceAccess, context: FirTowerDataContext) {
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 ecdb7c11..3fdffa6 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
@@ -11,17 +11,33 @@
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
-sealed class ResolutionMode(val forceFullCompletion: Boolean) {
- sealed class ContextDependent : ResolutionMode(forceFullCompletion = false) {
- companion object Default : ContextDependent() {
+sealed class ResolutionMode(
+ val forceFullCompletion: Boolean,
+ // Only true AugmentedAssignmentCallOption, don't run even slightest form of completion
+ val skipCompletion: Boolean = false,
+ // isReceiverOrTopLevel != forceFullCompletion -> Delegate
+ // Collect postponed calls
+ val isReceiverOrTopLevel: Boolean = forceFullCompletion,
+) {
+ sealed class ContextDependent(
+ skipCompletion: Boolean = false,
+ isReceiverOrTopLevel: Boolean,
+ ) : ResolutionMode(forceFullCompletion = false, skipCompletion, isReceiverOrTopLevel) {
+ companion object Default : ContextDependent(isReceiverOrTopLevel = false) {
override fun toString(): String = "ContextDependent"
}
- data object Delegate : ContextDependent()
+ data object Delegate : ContextDependent(isReceiverOrTopLevel = true)
+
+ data object AugmentedAssignmentCallOption : ContextDependent(isReceiverOrTopLevel = false, skipCompletion = true)
}
data object ContextIndependent : ResolutionMode(forceFullCompletion = true)
- data object ReceiverResolution : ResolutionMode(forceFullCompletion = true)
+
+ sealed class ReceiverResolution(val forCallableReference: Boolean) : ResolutionMode(forceFullCompletion = true) {
+ data object ForCallableReference : ReceiverResolution(forCallableReference = true)
+ companion object : ReceiverResolution(forCallableReference = false)
+ }
class WithExpectedType(
val expectedTypeRef: FirResolvedTypeRef,
@@ -42,7 +58,7 @@
fun copy(
mayBeCoercionToUnitApplied: Boolean = this.mayBeCoercionToUnitApplied,
- forceFullCompletion: Boolean = this.forceFullCompletion
+ forceFullCompletion: Boolean = this.forceFullCompletion,
): WithExpectedType = WithExpectedType(
expectedTypeRef, mayBeCoercionToUnitApplied, expectedTypeMismatchIsReportedInChecker, fromCast, shouldBeStrictlyEnforced,
forceFullCompletion
@@ -97,11 +113,15 @@
is ResolutionMode.WithExpectedType -> expectedTypeRef.takeIf { !this.fromCast }
is ResolutionMode.ContextIndependent,
is ResolutionMode.AssignmentLValue,
- is ResolutionMode.ReceiverResolution -> components.noExpectedType
+ is ResolutionMode.ReceiverResolution,
+ -> components.noExpectedType
else -> null
}
-fun withExpectedType(expectedTypeRef: FirTypeRef, expectedTypeMismatchIsReportedInChecker: Boolean = false): ResolutionMode = when {
+fun withExpectedType(
+ expectedTypeRef: FirTypeRef,
+ expectedTypeMismatchIsReportedInChecker: Boolean = false,
+): ResolutionMode = when {
expectedTypeRef is FirResolvedTypeRef -> ResolutionMode.WithExpectedType(
expectedTypeRef,
expectedTypeMismatchIsReportedInChecker = expectedTypeMismatchIsReportedInChecker
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..8c0776d 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>")
@@ -164,6 +168,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.resolveSubCallArgument(
csBuilder: ConstraintSystemBuilder,
argument: FirResolvable,
@@ -190,7 +214,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,
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallInfo.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallInfo.kt
index c15557e..8721f66 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallInfo.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallInfo.kt
@@ -15,6 +15,7 @@
import org.jetbrains.kotlin.fir.expressions.FirFunctionCallOrigin
import org.jetbrains.kotlin.fir.expressions.builder.buildArgumentList
import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS
+import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.name.Name
@@ -37,6 +38,8 @@
val candidateForCommonInvokeReceiver: Candidate? = null,
+ val resolutionMode: ResolutionMode,
+
// Five properties for callable references only
val expectedType: ConeKotlinType? = null,
val outerCSBuilder: ConstraintSystemBuilder? = null,
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..477d2df 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 {
+ processAllSubsystemsFromExpression(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,41 @@
}
}
-fun PostponedArgumentsAnalyzerContext.addSubsystemFromExpression(statement: FirStatement): Boolean {
+fun processAllSubsystemsFromExpression(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 -> processAllSubsystemsFromExpression(statement.selector, processor)
+ is FirWrappedArgumentExpression -> processAllSubsystemsFromExpression(statement.expression, processor)
+ is FirBlock -> {
+ var wasAny = false
+
+ // Might be `.any {` call, but we should process all the items
+ statement.returnExpressions().forEach {
+ if (processAllSubsystemsFromExpression(it, processor)) {
+ wasAny = true
+ }
+ }
+
+ wasAny
+ }
else -> false
}
}
+fun PostponedArgumentsAnalyzerContext.addSubsystemFromExpression(statement: FirStatement): Boolean {
+ return processAllSubsystemsFromExpression(statement, this::addOtherSystem)
+}
+
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 4443d1f..c77344f 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
@@ -266,6 +266,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>>,
@@ -561,7 +594,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..de09e73 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 == ConstraintSystemCompletionMode.FULL || completionMode == ConstraintSystemCompletionMode.PARTIAL_PCLA) {
// 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.PARTIAL_PCLA) {
+ // 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.PARTIAL_PCLA) {
+ 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 == ConstraintSystemCompletionMode.FULL || completionMode == ConstraintSystemCompletionMode.PARTIAL_PCLA) {
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.findNextReadyVariableForParameterTypeIfNeeded(
+ this,
+ argument,
+ postponedArguments,
+ topLevelType,
+ dependencyProvider,
+ ) ?: continue
+
+ assert(!variableForFixation.hasProperConstraint) {
+ "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 != ConstraintSystemCompletionMode.FULL && completionMode != ConstraintSystemCompletionMode.PARTIAL_PCLA) 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,12 +287,8 @@
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)
@@ -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
-
+ val variableForFixation =
+ findFirstVariableForFixation(false, topLevelAtoms, postponedArguments, completionMode, topLevelType)
+ ?: break
assert(!variableForFixation.hasProperConstraint) {
"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)
@@ -387,6 +391,12 @@
if (postponedAtom.mightNeedAdditionalResolution) {
postponedAtom.collectNotFixedVariables()
}
+
+ (postponedAtom.resultingReference as? FirNamedReferenceWithCandidate)?.candidate?.let { referenceCandidate ->
+ referenceCandidate.freshVariables.mapNotNullTo(result) { typeVariable ->
+ typeVariable.toTypeConstructor()
+ }
+ }
}
// ResolvedCallAtom?
// ResolvedCallableReferenceArgumentAtom?
@@ -399,30 +409,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 +570,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 17395d3..3196ec4 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
@@ -31,6 +31,7 @@
import org.jetbrains.kotlin.fir.resolve.transformers.replaceLambdaArgumentInvocationKinds
import org.jetbrains.kotlin.fir.resolve.typeFromCallee
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
@@ -41,14 +42,13 @@
import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
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.TypeConstructorMarker
import org.jetbrains.kotlin.types.model.safeSubstitute
import org.jetbrains.kotlin.utils.addToStdlib.runIf
class FirCallCompleter(
private val transformer: FirAbstractBodyResolveTransformerDispatcher,
- private val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents
+ private val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents,
) {
private val session = components.session
private val inferenceSession
@@ -78,6 +78,8 @@
resolutionMode,
)
+ if (resolutionMode.skipCompletion) return CompletionResult(call, false)
+
val completionMode = candidate.computeCompletionMode(
session.inferenceComponents, resolutionMode, initialType
).let {
@@ -89,10 +91,17 @@
// 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 {
+ inferenceSession is FirPCLAInferenceSession -> when {
+ inferenceSession.shouldAvoidFullCompletion(call) && it == ConstraintSystemCompletionMode.FULL -> ConstraintSystemCompletionMode.PARTIAL_PCLA
+ else -> it
+ }
+ else -> when {
+ it == ConstraintSystemCompletionMode.FULL && inferenceSession.shouldAvoidFullCompletion(call) -> ConstraintSystemCompletionMode.PARTIAL
+ else -> it
+ }
+ }
}
val analyzer = createPostponedArgumentsAnalyzer(transformer.resolutionContext)
@@ -121,15 +130,16 @@
inferenceSession.addCompletedCall(completedCall, candidate)
CompletionResult(completedCall, true)
} else {
- inferenceSession.processPartiallyResolvedCall(call, resolutionMode)
+ inferenceSession.processPartiallyResolvedCall(call, resolutionMode, completionMode)
CompletionResult(call, false)
}
}
- ConstraintSystemCompletionMode.PARTIAL -> {
+ ConstraintSystemCompletionMode.PARTIAL, ConstraintSystemCompletionMode.PARTIAL_PCLA -> {
runCompletionForCall(candidate, completionMode, call, initialType, analyzer)
- if (inferenceSession is FirDelegatedPropertyInferenceSession) {
- inferenceSession.processPartiallyResolvedCall(call, resolutionMode)
+
+ if (inferenceSession is FirDelegatedPropertyInferenceSession || inferenceSession is FirPCLAInferenceSession) {
+ inferenceSession.processPartiallyResolvedCall(call, resolutionMode, completionMode)
}
CompletionResult(call, false)
@@ -147,7 +157,15 @@
if (resolutionMode !is ResolutionMode.WithExpectedType) return
val expectedType = resolutionMode.expectedTypeRef.coneTypeSafe<ConeKotlinType>() ?: return
- val system = candidate.system
+ val position = ConeExpectedTypeConstraintPosition(candidate.callInfo.callSite)
+
+ val isCandidateSimpleVariable = (candidate.symbol as? FirVariableSymbol)?.typeParameterSymbols?.isEmpty() == true
+
+ val system = when {
+ inferenceSession is FirPCLAInferenceSession && isCandidateSimpleVariable ->
+ (inferenceSession as FirPCLAInferenceSession).currentCommonSystem
+ else -> candidate.system
+ }
when {
// If type mismatch is assumed to be reported in the checker, we should not add a subtyping constraint that leads to error.
// Because it might make resulting type correct while, it's hopefully would be more clear if we let the call be inferred without
@@ -156,25 +174,25 @@
// the resulting expression type cannot be inferred to something that is a subtype of `expectedType`,
// thus the diagnostic should be reported.
!resolutionMode.shouldBeStrictlyEnforced || resolutionMode.expectedTypeMismatchIsReportedInChecker -> {
- system.addSubtypeConstraintIfCompatible(initialType, expectedType, ConeExpectedTypeConstraintPosition)
+ system.addSubtypeConstraintIfCompatible(initialType, expectedType, position)
}
resolutionMode.fromCast -> {
if (candidate.isFunctionForExpectTypeFromCastFeature()) {
system.addSubtypeConstraint(
initialType, expectedType,
- ConeExpectedTypeConstraintPosition,
+ position,
)
}
}
!expectedType.isUnitOrFlexibleUnit || !resolutionMode.mayBeCoercionToUnitApplied -> {
- system.addSubtypeConstraint(initialType, expectedType, ConeExpectedTypeConstraintPosition)
+ system.addSubtypeConstraint(initialType, expectedType, position)
}
system.notFixedTypeVariables.isEmpty() -> return
expectedType.isUnit -> {
- system.addEqualityConstraintIfCompatible(initialType, expectedType, ConeExpectedTypeConstraintPosition)
+ system.addEqualityConstraintIfCompatible(initialType, expectedType, position)
}
else -> {
- system.addSubtypeConstraintIfCompatible(initialType, expectedType, ConeExpectedTypeConstraintPosition)
+ system.addSubtypeConstraintIfCompatible(initialType, expectedType, position)
}
}
}
@@ -184,8 +202,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(
@@ -195,13 +213,13 @@
initialType,
transformer.resolutionContext
) {
- analyzer.analyze(candidate.system, it, candidate, completionMode)
+ analyzer.analyze(candidate.system, it, candidate)
}
}
fun prepareLambdaAtomForFactoryPattern(
atom: ResolvedLambdaAtom,
- candidate: Candidate
+ candidate: Candidate,
) {
val returnVariable = ConeTypeVariableForLambdaReturnType(atom.atom, "_R")
val csBuilder = candidate.system.getBuilder()
@@ -222,7 +240,7 @@
fun createCompletionResultsWriter(
substitutor: ConeSubstitutor,
- mode: FirCallCompletionResultsWriterTransformer.Mode = FirCallCompletionResultsWriterTransformer.Mode.Normal
+ mode: FirCallCompletionResultsWriterTransformer.Mode = FirCallCompletionResultsWriterTransformer.Mode.Normal,
): FirCallCompletionResultsWriterTransformer {
return FirCallCompletionResultsWriterTransformer(
session, components.scopeSession, substitutor, components.returnTypeCalculator,
@@ -252,8 +270,9 @@
contextReceivers: List<ConeKotlinType>,
parameters: List<ConeKotlinType>,
expectedReturnType: ConeKotlinType?,
- stubsForPostponedVariables: Map<TypeVariableMarker, StubTypeMarker>,
- candidate: Candidate
+ candidate: Candidate,
+ notFixedTypeVariablesInInputTypes: Set<TypeConstructorMarker>,
+ currentSubstitutor: ConeSubstitutor,
): ReturnArgumentsAnalysisResult {
val lambdaArgument: FirAnonymousFunction = lambdaAtom.atom
val needItParam = lambdaArgument.valueParameters.isEmpty() && parameters.size == 1
@@ -356,29 +375,33 @@
} ?: components.noExpectedType
)
- val builderInferenceSession = runIf(stubsForPostponedVariables.isNotEmpty()) {
- @Suppress("UNCHECKED_CAST")
- FirBuilderInferenceSession(
- lambdaArgument,
- transformer.resolutionContext,
- stubsForPostponedVariables as Map<ConeTypeVariable, ConeStubType>
- )
- }
-
transformer.context.withAnonymousFunctionTowerDataContext(lambdaArgument.symbol) {
- if (builderInferenceSession != null) {
- transformer.context.withInferenceSession(builderInferenceSession) {
- lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef))
+ val pclaInferenceSession =
+ runIf(notFixedTypeVariablesInInputTypes.isNotEmpty()) {
+ candidate.lambdasAnalyzedWithPCLA += lambdaArgument
+
+ FirPCLAInferenceSession(candidate, session.inferenceComponents)
+ }
+
+ 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))
+ transformer.context.inferenceSession.runLambdaCompletion(candidate) {
+ 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, null)
}
}
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..c9bb656 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,85 @@
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 <T> shouldAvoidFullCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement {
+ if (!call.isAnyOfDelegateOperators()) return false
+ requireCallIsDelegateOperator(call)
+ return !wasCompletionRun
+ }
- 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 +94,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.PARTIAL_PCLA,
+ 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,17 +184,16 @@
}
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))
}
}
@@ -118,11 +201,18 @@
return notCompletedCalls
}
- fun createFinalSubstitutor(): ConeSubstitutor =
- currentConstraintSystem.asReadOnlyStorage()
- .buildAbstractResultingSubstitutor(components.session.typeContext) as ConeSubstitutor
-
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
}
+
+fun FirElement.isAnyOfDelegateOperators(): Boolean {
+ if (this is FirPropertyAccessExpression) {
+ val originalCall = this.candidate()?.callInfo?.callSite as? FirFunctionCall ?: return false
+ return originalCall.isAnyOfDelegateOperators()
+ }
+
+ if (this !is FirFunctionCall || origin != FirFunctionCallOrigin.Operator) return false
+ val name = calleeReference.name
+ return name == OperatorNameConventions.PROVIDE_DELEGATE || name == OperatorNameConventions.GET_VALUE || name == OperatorNameConventions.SET_VALUE
+}
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt
index 73b4119..fa6e083 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,21 +5,53 @@
package org.jetbrains.kotlin.fir.resolve.inference
+import org.jetbrains.kotlin.fir.FirElement
+import org.jetbrains.kotlin.fir.expressions.FirExpression
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> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true
+
+ override fun <T> processPartiallyResolvedCall(
+ call: T,
+ resolutionMode: ResolutionMode,
+ completionMode: ConstraintSystemCompletionMode,
+ ) where T : FirResolvable, T : FirStatement {
+ // Do nothing
+ }
+
+ override fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {
+ // Do nothing
+ }
+ }
+
+ @JvmStatic
+ protected fun prepareSharedBaseSystem(
+ outerSystem: NewConstraintSystemImpl,
+ components: InferenceComponents,
+ ): NewConstraintSystemImpl {
+ return components.createConstraintSystem().apply {
+ addOuterSystem(outerSystem.currentStorage())
+ }
+ }
}
+ open fun <T> runLambdaCompletion(candidate: Candidate, block: () -> T): T = block()
+
+ open fun <T> runCallableReferenceResolution(candidate: Candidate, block: () -> T): T = block()
+
+ open fun handleQualifiedAccess(qualifiedAccessExpression: FirExpression, data: ResolutionMode) {}
+
abstract fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement
/**
@@ -28,29 +60,17 @@
*/
open fun <T> shouldAvoidFullCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = false
- abstract fun <T> processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement
+ abstract fun <T> processPartiallyResolvedCall(
+ call: T,
+ resolutionMode: ResolutionMode,
+ completionMode: ConstraintSystemCompletionMode
+ ) where T : FirResolvable, T : FirStatement
+
abstract fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement
- abstract fun inferPostponedVariables(
- lambda: ResolvedLambdaAtom,
- constraintSystemBuilder: ConstraintSystemBuilder,
- completionMode: ConstraintSystemCompletionMode,
- candidate: Candidate
- ): Map<ConeTypeVariableTypeConstructor, ConeKotlinType>?
-
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..632737f
--- /dev/null
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirPCLAInferenceSession.kt
@@ -0,0 +1,306 @@
+/*
+ * 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.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,
+) : FirInferenceSession() {
+
+ var currentCommonSystem = prepareSharedBaseSystem(outerCandidate.system, inferenceComponents)
+ private set
+
+ private val qualifiedAccessesToProcess = mutableSetOf<FirExpression>()
+
+ override fun <T> shouldAvoidFullCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement {
+ val candidate = call.candidate() ?: return false
+ return candidate.usedOuterCs /*&& candidate.postponedAtoms.isEmpty()*/ /*call.candidate()?.usedOuterCs == true*/
+ }
+
+ // Should write completion results
+ override fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement =
+ call.candidate()?.usedOuterCs != true
+
+ override fun handleQualifiedAccess(qualifiedAccessExpression: FirExpression, data: ResolutionMode) {
+ // Callable references are being processed together with containing its call
+ if (qualifiedAccessExpression is FirCallableReferenceAccess) return
+
+ if (qualifiedAccessExpression.resolvedType.containsNotFixedTypeVariables() && (qualifiedAccessExpression as? FirResolvable)?.candidate() == null) {
+ qualifiedAccessesToProcess.add(qualifiedAccessExpression)
+
+ if (qualifiedAccessExpression is FirSmartCastExpression) {
+ handleQualifiedAccess(qualifiedAccessExpression.originalExpression, data)
+ }
+
+ qualifiedAccessExpression.updateReturnTypeWithCurrentSubstitutor(data)
+ }
+ }
+
+ override fun <T> runLambdaCompletion(candidate: Candidate, block: () -> T): T {
+ return runWithSpecifiedCurrentCommonSystem(candidate.system, block)
+ }
+
+ 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.isNotTrivial()) 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 (!resolutionMode.isReceiverOrTopLevel) return
+
+ 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>? {
+ // outerYield_1_3.kt
+ // org.jetbrains.kotlin.test.runners.FirPsiOldFrontendDiagnosticsTestGenerated.TestsWithStdLib.Coroutines.RestrictSuspension.testOuterYield_1_3
+ //if ("".hashCode() == 0) return null
+ 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)
+ }
+
+ private fun Candidate.isNotTrivial(): Boolean =
+ usedOuterCs
+
+ override fun baseConstraintStorageForCandidate(candidate: Candidate): ConstraintStorage? {
+ if (candidate.needsToBePostponed()) return currentCommonSystem.currentStorage()
+ if (candidate.callInfo.arguments.any { it.isLambda() }) return currentCommonSystem.currentStorage()
+ // TODO: context receivers
+ 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 {
+ // For invokeExtension calls
+ if (callInfo.candidateForCommonInvokeReceiver != null) {
+ callInfo.arguments.getOrNull(0)
+ ?.takeIf { it is FirQualifiedAccessExpression && it !in qualifiedAccessesToProcess }
+ ?.let { handleQualifiedAccess(it, ResolutionMode.ContextDependent) }
+ }
+
+ if (dispatchReceiver?.isReceiverPostponed() == true) return true
+ if (givenExtensionReceiverOptions.any { it.isReceiverPostponed() }) return true
+ if (callInfo.arguments.any { it.isArgumentAmongPostponedQualifiedAccess() }) return true
+ if (callInfo.resolutionMode is ResolutionMode.ContextDependent.Delegate) return true
+ // For assignments
+ 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.isArgumentAmongPostponedQualifiedAccess(): Boolean {
+ if (this is FirNamedArgumentExpression) return expression.isArgumentAmongPostponedQualifiedAccess()
+ return this in qualifiedAccessesToProcess
+ }
+
+ 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(element))
+ }
+
+ // TODO: Get rid of them
+
+ override fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {}
+}
+
+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 bb3f84b..2110e41 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,15 @@
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.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 inferenceSession: FirInferenceSession?,
)
interface LambdaAnalyzer {
@@ -35,8 +37,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,
+ notFixedTypeVariablesInInputTypes: Set<TypeConstructorMarker>,
+ currentSubstitutor: ConeSubstitutor,
): ReturnArgumentsAnalysisResult
}
@@ -44,21 +47,20 @@
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)
is LambdaWithTypeVariableAsExpectedTypeAtom ->
- analyzeLambda(c, argument.transformToResolvedLambda(c.getBuilder(), resolutionContext), candidate, completionMode)
+ analyzeLambda(c, argument.transformToResolvedLambda(c.getBuilder(), resolutionContext), candidate)
is ResolvedCallableReferenceAtom -> processCallableReference(argument, candidate)
}
@@ -66,7 +68,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,7 +100,6 @@
c: PostponedArgumentsAnalyzerContext,
lambda: ResolvedLambdaAtom,
candidate: Candidate,
- completionMode: ConstraintSystemCompletionMode,
//diagnosticHolder: KotlinDiagnosticsHolder
): ReturnArgumentsAnalysisResult {
// TODO: replace with `require(!lambda.analyzed)` when KT-54767 will be fixed
@@ -106,9 +107,22 @@
return ReturnArgumentsAnalysisResult(lambda.returnStatements, inferenceSession = 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 +140,27 @@
else -> null
}
+ val notFixedTypeVariablesInInputTypes =
+ lambda.inputTypes
+ .flatMap {
+ with(c) { it.extractTypeVariables() }
+ }.filterTo(mutableSetOf()) { it in c.notFixedTypeVariables }
+
val results = lambdaAnalyzer.analyzeAndGetLambdaReturnArguments(
lambda,
receiver,
contextReceivers,
parameters,
expectedTypeForReturnArguments,
- stubsForPostponedVariables,
- candidate
+ candidate,
+ notFixedTypeVariablesInInputTypes,
+ currentSubstitutor,
)
applyResultsOfAnalyzedLambdaToCandidateSystem(
c,
lambda,
candidate,
results,
- completionMode,
::substitute
)
return results
@@ -151,10 +171,9 @@
lambda: ResolvedLambdaAtom,
candidate: Candidate,
results: ReturnArgumentsAnalysisResult,
- completionMode: ConstraintSystemCompletionMode,
- substitute: (ConeKotlinType) -> ConeKotlinType = c.createSubstituteFunctorForLambdaAnalysis()
+ substitute: (ConeKotlinType) -> ConeKotlinType = c.createSubstituteFunctorForLambdaAnalysis(),
) {
- val (returnArguments, inferenceSession) = results
+ val (returnArguments, _) = results
val checkerSink: CheckerSink = CheckerSinkImpl(candidate)
val builder = c.getBuilder()
@@ -169,11 +188,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 +195,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 +240,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 {
@@ -244,7 +253,7 @@
csBuilder: ConstraintSystemBuilder,
context: ResolutionContext,
expectedType: ConeKotlinType? = null,
- returnTypeVariable: ConeTypeVariableForLambdaReturnType? = null
+ returnTypeVariable: ConeTypeVariableForLambdaReturnType? = null,
): ResolvedLambdaAtom {
val fixedExpectedType = (csBuilder.buildCurrentSubstitutor() as ConeSubstitutor)
.substituteOrSelf(expectedType ?: this.expectedType)
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 2bcd961..78337dc 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,13 +9,13 @@
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
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildSamConversionExpression
-import org.jetbrains.kotlin.fir.expressions.impl.FirPropertyAccessExpressionImpl
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.references.builder.buildResolvedCallableReference
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
@@ -23,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
@@ -33,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
@@ -43,7 +47,6 @@
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.resolve.calls.inference.model.InferredEmptyIntersection
-import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
import org.jetbrains.kotlin.types.Variance
@@ -65,6 +68,15 @@
private val mode: Mode = Mode.Normal,
) : FirAbstractTreeTransformer<ExpectedArgumentType?>(phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) {
+ // TODO: do we really need to transform all children for unexpected element?
+ override fun <E : FirElement> transformElement(element: E, data: ExpectedArgumentType?): E {
+ return super.transformElement(element, data)
+ }
+
+ override fun transformTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: ExpectedArgumentType?): FirStatement {
+ return typeOperatorCall
+ }
+
private fun finallySubstituteOrNull(type: ConeKotlinType): ConeKotlinType? {
val result = finalSubstitutor.substituteOrNull(type)
if (result == null && type is ConeIntegerLiteralType) {
@@ -98,6 +110,8 @@
): T {
val subCandidate = calleeReference.candidate
+ val additionalDiagnostic = checkExplicitReceiver(calleeReference.candidate)
+
subCandidate.updateSubstitutedMemberIfReceiverContainsTypeVariable()
val declaration = subCandidate.symbol.fir
@@ -138,8 +152,14 @@
extensionReceiver = extensionReceiver?.transformSingle(integerOperatorApproximator, expectedExtensionReceiverType)
}
- (qualifiedAccessExpression as? FirQualifiedAccessExpression)?.apply {
- replaceCalleeReference(calleeReference.toResolvedReference())
+ if (subCandidate.usedOuterCs) {
+ val updaterForThisReferences = TypeUpdaterForThisReferences()
+ dispatchReceiver = dispatchReceiver?.transformSingle(updaterForThisReferences, null)
+ extensionReceiver = extensionReceiver?.transformSingle(updaterForThisReferences, null)
+ }
+
+ qualifiedAccessExpression.apply {
+ replaceCalleeReference(calleeReference.toResolvedReference(additionalDiagnostic))
replaceDispatchReceiver(dispatchReceiver)
replaceExtensionReceiver(extensionReceiver)
}
@@ -160,6 +180,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
}
@@ -212,7 +247,7 @@
private fun FirBasedSymbol<*>.updateSubstitutedMemberIfReceiverContainsTypeVariable(): 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) return null
val fir = fir
if (fir !is FirCallableDeclaration) return null
@@ -229,6 +264,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)
@@ -497,8 +548,12 @@
context.file.source
)
- val resolvedReference = when (calleeReference) {
- is FirErrorReferenceWithCandidate -> calleeReference.toErrorReference(calleeReference.diagnostic)
+
+ val additionalDiagnostic = checkExplicitReceiver(subCandidate)
+
+ val resolvedReference = when {
+ calleeReference is FirErrorReferenceWithCandidate -> calleeReference.toErrorReference(calleeReference.diagnostic)
+ additionalDiagnostic != null -> calleeReference.toErrorReference(additionalDiagnostic)
else -> buildResolvedCallableReference {
source = calleeReference.source
name = calleeReference.name
@@ -508,10 +563,19 @@
}
}
+ var dispatchReceiver = subCandidate.dispatchReceiverExpression()
+ var extensionReceiver = subCandidate.chosenExtensionReceiverExpression()
+
+ if (subCandidate.usedOuterCs) {
+ val updaterForThisReferences = TypeUpdaterForThisReferences()
+ 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)
}
@@ -531,11 +595,7 @@
qualifiedAccessExpression: FirQualifiedAccessExpression,
data: Any?,
): FirStatement {
- val originalType = qualifiedAccessExpression.resolvedType
- val substitutedReceiverType = finallySubstituteOrNull(originalType) ?: return qualifiedAccessExpression
- qualifiedAccessExpression.replaceConeTypeOrNull(substitutedReceiverType)
- session.lookupTracker?.recordTypeResolveAsLookup(substitutedReceiverType, qualifiedAccessExpression.source, context.file.source)
- return qualifiedAccessExpression
+ return transformTypeRefForQualifiedAccess(qualifiedAccessExpression)
}
override fun transformPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression, data: Any?): FirStatement {
@@ -543,6 +603,25 @@
}
}
+ private inner class TypeUpdaterForThisReferences : 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)
+ }
+ }
+
+ 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
+ }
+
private fun FirTypeRef.substitute(candidate: Candidate): ConeKotlinType {
return coneType.substitute(candidate)
}
@@ -705,6 +784,15 @@
needUpdateLambdaType = true
}
+ for (valueParameter in anonymousFunction.valueParameters) {
+ val initialParameterType = valueParameter.returnTypeRef.coneTypeSafe<ConeKotlinType>()
+ val resultParameterType = initialParameterType?.let { finallySubstituteOrNull(it) }
+ if (resultParameterType != null) {
+ valueParameter.replaceReturnTypeRef(valueParameter.returnTypeRef.resolvedTypeFromPrototype(resultParameterType))
+ needUpdateLambdaType = true
+ }
+ }
+
val initialReturnType = anonymousFunction.returnTypeRef.coneTypeSafe<ConeKotlinType>()
val expectedReturnType = initialReturnType?.let { finallySubstituteOrSelf(it) }
?: expectedType?.returnType(session) as? ConeClassLikeType
@@ -916,8 +1004,9 @@
// TODO: report warning with a checker and return true here only in case of errors, KT-59676
private fun FirNamedReferenceWithCandidate.hasAdditionalResolutionErrors(): Boolean =
candidate.system.errors.any { it is InferredEmptyIntersection }
+ || candidate.diagnostics.any { it is InferenceError && it.constraintError is InferredEmptyIntersection }
- private fun FirNamedReferenceWithCandidate.toResolvedReference(): FirNamedReference {
+ private fun FirNamedReferenceWithCandidate.toResolvedReference(additionalDiagnostic: ConeDiagnostic? = null): FirNamedReference {
val errorDiagnostic = when {
this is FirErrorReferenceWithCandidate -> this.diagnostic
!candidate.currentApplicability.isSuccess -> ConeInapplicableCandidateError(candidate.currentApplicability, candidate)
@@ -931,7 +1020,7 @@
// NB: these additional errors might not lead to marking candidate unsuccessful because it may be a warning in FE 1.0
// We consider those warnings as errors in FIR
hasAdditionalResolutionErrors() -> ConeConstraintSystemHasContradiction(candidate)
- else -> null
+ else -> additionalDiagnostic
}
return when (errorDiagnostic) {
@@ -944,6 +1033,12 @@
else -> toErrorReference(errorDiagnostic)
}
}
+
+ private fun checkExplicitReceiver(candidate: Candidate): ConeDiagnostic? {
+ return candidate.diagnostics.firstIsInstanceOrNull<TypeVariableAsExplicitReceiver>()?.let {
+ ConeUsingTypeVariableAsExplicitReceiver(it.explicitReceiver, it.typeParameter)
+ }
+ }
}
sealed class ExpectedArgumentType {
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSyntheticCallGenerator.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSyntheticCallGenerator.kt
index d4cbc11..54d1f57 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSyntheticCallGenerator.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSyntheticCallGenerator.kt
@@ -374,7 +374,7 @@
context: ResolutionContext,
resolutionMode: ResolutionMode,
): FirNamedReferenceWithCandidate {
- val callInfo = generateCallInfo(callSite, name, argumentList, callKind, context, resolutionMode)
+ val callInfo = generateCallInfo(callSite, name, argumentList, callKind, resolutionMode)
val candidate = generateCandidate(callInfo, function, context)
val applicability = components.resolutionStageRunner.processCandidate(candidate, context)
val source = callSite.source?.fakeElement(KtFakeSourceElementKind.SyntheticCall)
@@ -406,7 +406,6 @@
name: Name,
argumentList: FirArgumentList,
callKind: CallKind,
- context: ResolutionContext,
resolutionMode: ResolutionMode,
) = CallInfo(
callSite = callSite,
@@ -420,7 +419,6 @@
session = session,
containingFile = components.file,
containingDeclarations = components.containingDeclarations,
- isDelegateExpression = context.bodyResolveContext.isDelegateExpression(callSite),
resolutionMode = resolutionMode,
)
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..e1a3b6b 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
}
@@ -343,7 +340,14 @@
@OptIn(PrivateForInline::class)
inline fun <T> withAnonymousFunctionTowerDataContext(symbol: FirAnonymousFunctionSymbol, f: () -> T): T {
- return withTemporaryRegularContext(specialTowerDataContexts.getAnonymousFunctionContext(symbol), f)
+ return withTemporaryRegularContext(specialTowerDataContexts.getAnonymousFunctionContext(symbol)) {
+ val inferenceSession = specialTowerDataContexts.getAnonymousFunctionInferenceSession(symbol)
+ if (inferenceSession != null) {
+ withInferenceSession(inferenceSession) { f() }
+ } else {
+ f()
+ }
+ }
}
@OptIn(PrivateForInline::class)
@@ -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.takeIf { it is FirPCLAInferenceSession }
+ )
}
@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,
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt
index 49a36a1..857d2b2 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt
@@ -17,7 +17,6 @@
import org.jetbrains.kotlin.fir.resolve.transformers.FirSyntheticCallGenerator
import org.jetbrains.kotlin.fir.resolve.transformers.FirWhenExhaustivenessTransformer
import org.jetbrains.kotlin.fir.resolve.withExpectedType
-import org.jetbrains.kotlin.fir.resolve.withExpectedType
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visitors.transformSingle
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 ee21c51..e0c5386 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
@@ -332,86 +331,76 @@
delegate: FirExpression,
shouldResolveEverything: Boolean,
) {
- val isImplicitTypedProperty = property.returnTypeRef is FirImplicitTypeRef
+ require(delegate 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, and add potentially partially resolved call to inference session
+ // (that is why we use ContextDependent.Delegate instead of plain ContextDependent)
+ 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(delegate)
} else {
context.forPropertyInitializer {
- property.transformDelegate(transformer, ResolutionMode.ContextDependent.Delegate)
+ transformDelegateExpression(delegate)
}
}
+ context.withInferenceSession(
+ FirDelegatedPropertyInferenceSession(
+ resolutionContext,
+ callCompleter,
+ delegateExpression,
+ )
+ ) {
+ property.replaceDelegate(delegate.computeResultingDelegateFieldInitializer(delegateExpression))
+
// 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.
// 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(delegate)
}
}
-
- // `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.delegateProvider as FirFunctionCall
- provideDelegateCall.replaceExplicitReceiver(delegateExpression)
+ private fun FirWrappedDelegateExpression.computeResultingDelegateFieldInitializer(
+ resolvedDelegateExpression: FirExpression,
+ ): FirExpression {
+ val provideDelegateCall = delegateProvider as 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 +427,34 @@
return provideDelegateCall
}
+ return resolvedDelegateExpression
+ }
+
+ 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 = transformDelegateExpression(wrappedDelegateExpression)
+
+ val provideDelegateCall = wrappedDelegateExpression.delegateProvider as FirFunctionCall
+ provideDelegateCall.replaceExplicitReceiver(delegateExpression)
+
// Select delegate expression otherwise
return delegateExpression
}
+ 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 +494,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 +502,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 +514,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 5b8cb0b..0579d1e 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
@@ -30,7 +30,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
@@ -90,7 +89,7 @@
transformQualifiedAccessExpression(qualifiedAccessExpression, data, isUsedAsReceiver = false, isUsedAsGetClassReceiver = false)
}
- fun transformQualifiedAccessExpression(
+ private fun transformQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression,
data: ResolutionMode,
isUsedAsReceiver: Boolean,
@@ -170,6 +169,7 @@
}
}
+ // TODO: Smart casts??
// If we're resolving the LHS of an assignment, skip DFA to prevent the access being treated as a variable read and
// smart-casts being applied.
if (data !is ResolutionMode.AssignmentLValue) {
@@ -186,6 +186,11 @@
}
}
}
+
+ if (result is FirExpression) {
+ context.inferenceSession.handleQualifiedAccess(result, data)
+ }
+
return result
}
@@ -419,6 +424,7 @@
functionCall.transformAnnotations(transformer, data)
functionCall.replaceLambdaArgumentInvocationKinds(session)
functionCall.transformTypeArguments(transformer, ResolutionMode.ContextIndependent)
+ val resolvingAugmentedAssignment = data == ResolutionMode.ContextDependent.AugmentedAssignmentCallOption
val withTransformedArguments = if (!resolvingAugmentedAssignment) {
dataFlowAnalyzer.enterCallArguments(functionCall, functionCall.arguments)
// In provideDelegate mode the explicitReceiver is already resolved
@@ -600,17 +606,13 @@
// x.plusAssign(y)
val assignOperatorCall = generator.createAssignOperatorCall()
- val resolvedAssignCall = resolveCandidateForAssignmentOperatorCall {
- assignOperatorCall.transformSingle(this, ResolutionMode.ContextDependent)
- }
+ val resolvedAssignCall = assignOperatorCall.resolveCandidateForAssignmentOperatorCall()
val assignCallReference = resolvedAssignCall.calleeReference as? FirNamedReferenceWithCandidate
val assignIsSuccessful = assignCallReference?.isError == false
// x = x + y
val simpleOperatorCall = generator.createSimpleOperatorCall()
- val resolvedOperatorCall = resolveCandidateForAssignmentOperatorCall {
- simpleOperatorCall.transformSingle(this, ResolutionMode.ContextDependent)
- }
+ val resolvedOperatorCall = simpleOperatorCall.resolveCandidateForAssignmentOperatorCall()
val operatorCallReference = resolvedOperatorCall.calleeReference as? FirNamedReferenceWithCandidate
val operatorIsSuccessful = operatorCallReference?.isError == false
@@ -823,22 +825,12 @@
return equalityOperatorCall
}
- private var resolvingAugmentedAssignment: Boolean = false
-
- private inline fun <T> resolveCandidateForAssignmentOperatorCall(block: () -> T): T {
- assert(!resolvingAugmentedAssignment)
- resolvingAugmentedAssignment = true
- return try {
- context.withInferenceSession(InferenceSessionForAssignmentOperatorCall) {
- block()
- }
- } finally {
- resolvingAugmentedAssignment = false
- }
- }
-
- private object InferenceSessionForAssignmentOperatorCall : FirStubInferenceSession() {
- override fun <T> shouldRunCompletion(call: T): Boolean where T : FirStatement, T : FirResolvable = false
+ private fun FirFunctionCall.resolveCandidateForAssignmentOperatorCall(): FirFunctionCall {
+ return transformFunctionCallInternal(
+ this,
+ ResolutionMode.ContextDependent.AugmentedAssignmentCallOption,
+ provideDelegate = false
+ ) as FirFunctionCall
}
private fun FirTypeRef.withTypeArgumentsForBareType(argument: FirExpression, operation: FirOperation): FirTypeRef {
@@ -964,7 +956,7 @@
.replaceArgumentList(checkNotNullCall.argumentList.transform(transformer, ResolutionMode.ContextDependent))
val (result, callCompleted) = callCompleter.completeCall(
- components.syntheticCallGenerator.generateCalleeForCheckNotNullCall(checkNotNullCall, resolutionContext), data
+ components.syntheticCallGenerator.generateCalleeForCheckNotNullCall(checkNotNullCall, resolutionContext, data), data
)
if (checkNotNullCall.arguments.firstOrNull()?.coneTypeOrNull !is ConeDynamicType) {
@@ -1046,6 +1038,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
@@ -1064,10 +1063,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)
@@ -1192,7 +1191,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)
}
@@ -1423,9 +1422,7 @@
// a.get(b).plusAssign(c)
val assignOperatorCall = generator.createAssignOperatorCall()
- val resolvedAssignCall = resolveCandidateForAssignmentOperatorCall {
- assignOperatorCall.transformSingle(this, ResolutionMode.ContextDependent)
- }
+ val resolvedAssignCall = assignOperatorCall.resolveCandidateForAssignmentOperatorCall()
val assignCallReference = resolvedAssignCall.calleeReference as? FirNamedReferenceWithCandidate
val assignIsSuccessful = assignCallReference?.isError == false
@@ -1626,9 +1623,7 @@
)
val operatorCall = generator.createSimpleOperatorCall()
- val resolvedOperatorCall = resolveCandidateForAssignmentOperatorCall {
- operatorCall.transformSingle(this, ResolutionMode.ContextDependent)
- }
+ val resolvedOperatorCall = operatorCall.resolveCandidateForAssignmentOperatorCall()
val setCall = GeneratorOfPlusAssignCalls.createFunctionCall(
OperatorNameConventions.SET,
@@ -1639,9 +1634,7 @@
*indicesQualifiedAccess.toTypedArray(), // indices
resolvedOperatorCall // a.get(b).plus(c)
)
- val resolvedSetCall = resolveCandidateForAssignmentOperatorCall {
- setCall.transformSingle(this, ResolutionMode.ContextDependent)
- }
+ val resolvedSetCall = setCall.resolveCandidateForAssignmentOperatorCall()
return AugmentedArraySetAsGetSetCallDesugaringInfo(
augmentedArraySetCall,
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 7d5830d..e64ac6a 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
@@ -156,6 +157,11 @@
object TypeParameterAsExpression : ResolutionDiagnostic(INAPPLICABLE)
+class TypeVariableAsExplicitReceiver(
+ val explicitReceiver: FirExpression,
+ val typeParameter: FirTypeParameter,
+) : ResolutionDiagnostic(RESOLVED)
+
class StubBuilderInferenceReceiver(
val typeParameterSymbol: FirTypeParameterSymbol
) : ResolutionDiagnostic(RESOLVED_WITH_ERROR)
diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt
index 94cc501..6e91bbb 100644
--- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt
+++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt
@@ -9,6 +9,7 @@
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.contracts.description.ConeContractDescriptionElement
+import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
import org.jetbrains.kotlin.fir.declarations.FirVariable
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnosticWithNullability
@@ -149,6 +150,13 @@
override val candidateSymbol: FirBasedSymbol<*> get() = candidate.symbol
}
+class ConeUsingTypeVariableAsExplicitReceiver(
+ val explicitReceiver: FirElement,
+ val typeParameter: FirTypeParameter,
+) : ConeDiagnostic {
+ override val reason: String get() = ""
+}
+
class ConeAmbiguityError(
val name: Name,
val applicability: CandidateApplicability,
diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/inference/model/FirConstraintPositionAndErrors.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/inference/model/FirConstraintPositionAndErrors.kt
index 37b1331..7cb17fe 100644
--- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/inference/model/FirConstraintPositionAndErrors.kt
+++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/inference/model/FirConstraintPositionAndErrors.kt
@@ -28,7 +28,7 @@
}
}
-object ConeExpectedTypeConstraintPosition : ExpectedTypeConstraintPosition<Nothing?>(null) {
+class ConeExpectedTypeConstraintPosition(topLevelCall: FirElement) : ExpectedTypeConstraintPosition<FirElement>(topLevelCall) {
override fun toString(): String = "ExpectedType for some call"
}
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..b0c9a22 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,10 @@
return IrTypeSubstitutor(emptyMap())
}
+ 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..2f5e92d 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
@@ -16,16 +16,10 @@
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
@@ -120,4 +114,10 @@
!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]
+ */
+ 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..899b85e 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
@@ -19,5 +19,6 @@
* ```
*/
PARTIAL,
+ PARTIAL_PCLA,
UNTIL_FIRST_LAMBDA
}
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..6206463 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,7 +536,7 @@
postponedArguments: List<PostponedResolvedAtomMarker>,
topLevelType: KotlinTypeMarker,
dependencyProvider: TypeVariableDependencyInformationProvider,
- resolvedAtomProvider: ResolvedAtomProvider
+ resolvedAtomProvider: ResolvedAtomProvider,
): Boolean = with(c) {
val expectedType = argument.run { (this as? PostponedAtomWithRevisableExpectedType)?.revisedExpectedType ?: expectedType }
@@ -541,7 +546,7 @@
postponedArguments,
topLevelType,
dependencyProvider,
- resolvedAtomProvider
+ resolvedAtomProvider,
)
if (wasFixedSomeVariable)
@@ -551,6 +556,27 @@
return false
}
+ fun findNextReadyVariableForParameterTypeIfNeeded(
+ c: Context,
+ argument: PostponedResolvedAtomMarker,
+ postponedArguments: List<PostponedResolvedAtomMarker>,
+ topLevelType: KotlinTypeMarker,
+ dependencyProvider: TypeVariableDependencyInformationProvider,
+ ): VariableFixationFinder.VariableForFixation? = with(c) {
+ val expectedType = argument.run { (this as? PostponedAtomWithRevisableExpectedType)?.revisedExpectedType ?: expectedType }
+
+ if (expectedType != null && expectedType.isFunctionOrKFunctionWithAnySuspendability()) {
+ return c.findNextVariableForParameterType(
+ expectedType,
+ dependencyProvider,
+ postponedArguments,
+ topLevelType,
+ )
+ }
+
+ return null
+ }
+
private fun Context.fixNextReadyVariableForParameterType(
type: KotlinTypeMarker,
postponedArguments: List<PostponedResolvedAtomMarker>,
@@ -558,15 +584,7 @@
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)
return false
@@ -589,6 +607,27 @@
return true
}
+ private fun Context.findNextVariableForParameterType(
+ type: KotlinTypeMarker,
+ dependencyProvider: TypeVariableDependencyInformationProvider,
+ postponedArguments: List<PostponedResolvedAtomMarker>,
+ topLevelType: KotlinTypeMarker,
+ ): VariableFixationFinder.VariableForFixation? {
+ val outerTypeVariables = outerTypeVariables ?: emptySet()
+ val relatedVariables = type.extractArgumentsForFunctionTypeOrSubtype()
+ .flatMap { getAllDeeplyRelatedTypeVariables(it, dependencyProvider) }
+ .filter { it !in outerTypeVariables }
+
+ return variableFixationFinder.findFirstVariableForFixation(
+ this,
+ relatedVariables,
+ postponedArguments,
+ ConstraintSystemCompletionMode.FULL,
+ topLevelType,
+ isRelevantToInputType = false,
+ )
+ }
+
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..deee490 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,8 @@
if (typesWithoutStubs.isNotEmpty()) {
commonSuperType = computeCommonSuperType(typesWithoutStubs)
+ } else if (outerSystemVariablesPrefixSize > 0) {
+ commonSuperType = createSubstitutionFromSubtypingStubTypesToTypeVariables().safeSubstitute(commonSuperType)
}
}
@@ -273,11 +277,23 @@
}
if (!atLeastOneProper) return emptyList()
- if (!atLeastOneNonProper) return lowerConstraintTypes
+
+ val noOuterTypeVariables = outerSystemVariablesPrefixSize == 0
+ if (!atLeastOneNonProper && noOuterTypeVariables) return lowerConstraintTypes
+
+ // Might be an optimization
+// if (!noOuterTypeVariables && lowerConstraintTypes.size == 1 && isProperTypeForFixation(lowerConstraintTypes.single())) {
+// return lowerConstraintTypes
+// }
val notFixedToStubTypesSubstitutor = buildNotFixedVariablesToStubTypesSubstitutor()
- return lowerConstraintTypes.map { if (isProperTypeForFixation(it)) it else notFixedToStubTypesSubstitutor.safeSubstitute(it) }
+ return lowerConstraintTypes.map {
+ if (noOuterTypeVariables && isProperTypeForFixation(it))
+ it
+ else
+ notFixedToStubTypesSubstitutor.safeSubstitute(it)
+ }
}
private fun Context.sinkIntegerLiteralTypes(types: List<KotlinTypeMarker>): List<KotlinTypeMarker> {
@@ -336,7 +352,7 @@
}
private fun Context.isProperTypeForFixation(type: KotlinTypeMarker): Boolean =
- isProperTypeForFixation(type) { isProperType(it) }
+ isProperTypeForFixation(type, { it in notFixedTypeVariables }) { 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/TypeCheckerStateForConstraintSystem.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeCheckerStateForConstraintSystem.kt
index 11ef367..5fc6df1 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeCheckerStateForConstraintSystem.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeCheckerStateForConstraintSystem.kt
@@ -143,7 +143,6 @@
// extract type variable only from type like Captured(out T)
private fun extractTypeVariableForSubtype(subType: KotlinTypeMarker, superType: KotlinTypeMarker): KotlinTypeMarker? =
with(extensionTypeContext) {
-
val typeMarker = subType.asSimpleType()?.asCapturedType() ?: return null
val projection = typeMarker.typeConstructorProjection()
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..80ccb2d 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,22 @@
computeRelatedToTopLevelType()
}
- fun isVariableRelatedToTopLevelType(variable: TypeConstructorMarker) = relatedToTopLevelType.contains(variable)
+ fun isVariableRelatedToTopLevelType(variable: TypeConstructorMarker) =
+ relatedToTopLevelType.contains(variable)
+
+
+ fun isDeeplyRelatedToOuterType(variable: TypeConstructorMarker): Boolean {
+ val outerTypeVariables = outerTypeVariables ?: return false
+ val myDependent = getDeeplyDependentVariables(variable) ?: return false
+ return myDependent.any { it in outerTypeVariables }
+ }
+
+ fun isShallowlyRelatedToOuterType(variable: TypeConstructorMarker): Boolean {
+ val outerTypeVariables = outerTypeVariables ?: return false
+ val myDependent = getShallowlyDependentVariables(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 f9c51d3..10c3ca1 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,17 +27,29 @@
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
}
@@ -45,7 +57,7 @@
data class VariableForFixation(
val variable: TypeConstructorMarker,
val hasProperConstraint: Boolean,
- val hasOnlyTrivialProperConstraint: Boolean = false
+ val hasOnlyTrivialProperConstraint: Boolean = false,
)
fun findFirstVariableForFixation(
@@ -54,11 +66,15 @@
postponedKtPrimitives: List<PostponedResolvedAtomMarker>,
completionMode: ConstraintSystemCompletionMode,
topLevelType: KotlinTypeMarker,
- ): VariableForFixation? = c.findTypeVariableForFixation(allTypeVariables, postponedKtPrimitives, completionMode, topLevelType)
+ isRelevantToInputType: Boolean = false,
+ ): VariableForFixation? =
+ c.findTypeVariableForFixation(allTypeVariables, postponedKtPrimitives, completionMode, topLevelType, isRelevantToInputType)
enum class TypeVariableFixationReadiness {
FORBIDDEN,
WITHOUT_PROPER_ARGUMENT_CONSTRAINT, // proper constraint from arguments -- not from upper bound for type parameters
+ SHALLOW_OUTER_TYPE_VARIABLE_DEPENDENCY,
+ DEEP_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>
WITH_TRIVIAL_OR_NON_PROPER_CONSTRAINTS, // proper trivial constraint from arguments, Nothing <: T
@@ -86,6 +102,7 @@
isTypeInferenceForSelfTypesSupported && areAllProperConstraintsSelfTypeBased(variable) ->
TypeVariableFixationReadiness.READY_FOR_FIXATION_DECLARED_UPPER_BOUND_WITH_SELF_TYPES
!variableHasProperArgumentConstraints(variable) -> TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT
+ dependencyProvider.isDeeplyRelatedToOuterType(variable) -> TypeVariableFixationReadiness.DEEP_OUTER_TYPE_VARIABLE_DEPENDENCY
hasDependencyToOtherTypeVariables(variable) -> TypeVariableFixationReadiness.WITH_COMPLEX_DEPENDENCY
variableHasTrivialOrNonProperConstraints(variable) -> TypeVariableFixationReadiness.WITH_TRIVIAL_OR_NON_PROPER_CONSTRAINTS
dependencyProvider.isVariableRelatedToAnyOutputType(variable) -> TypeVariableFixationReadiness.RELATED_TO_ANY_OUTPUT_TYPE
@@ -149,19 +166,24 @@
postponedArguments: List<PostponedResolvedAtomMarker>,
completionMode: ConstraintSystemCompletionMode,
topLevelType: KotlinTypeMarker,
+ isRelevantToInputType: Boolean,
): VariableForFixation? {
if (allTypeVariables.isEmpty()) return null
val dependencyProvider = TypeVariableDependencyInformationProvider(
- notFixedTypeVariables, postponedArguments, topLevelType.takeIf { completionMode == PARTIAL }, this
+ notFixedTypeVariables, postponedArguments, topLevelType.takeIf { completionMode == PARTIAL }, this,
)
val candidate =
allTypeVariables.maxByOrNull { getTypeVariableReadiness(it, dependencyProvider) } ?: return null
+ check(!isRelevantToInputType)
+
return when (getTypeVariableReadiness(candidate, dependencyProvider)) {
TypeVariableFixationReadiness.FORBIDDEN -> null
TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT -> VariableForFixation(candidate, false)
+ TypeVariableFixationReadiness.DEEP_OUTER_TYPE_VARIABLE_DEPENDENCY ->
+ VariableForFixation(candidate, hasProperConstraint = false)
TypeVariableFixationReadiness.WITH_TRIVIAL_OR_NON_PROPER_CONSTRAINTS ->
VariableForFixation(candidate, hasProperConstraint = true, hasOnlyTrivialProperConstraint = true)
@@ -193,12 +215,13 @@
&& !c.isNullabilityConstraint
private fun Context.isProperType(type: KotlinTypeMarker): Boolean =
- isProperTypeForFixation(type) { t -> !t.contains { isNotFixedRelevantVariable(it) } }
+ isProperTypeForFixation(type, { it in notFixedTypeVariables }) { 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 != null && typeVariablesThatAreCountedAsProperTypes!!.contains(key)) return false
+ return true
}
private fun Context.isReified(variable: TypeConstructorMarker): Boolean =
@@ -238,8 +261,15 @@
}
}
-inline fun TypeSystemInferenceExtensionContext.isProperTypeForFixation(type: KotlinTypeMarker, isProper: (KotlinTypeMarker) -> Boolean) =
- isProper(type) && extractProjectionsForAllCapturedTypes(type).all(isProper)
+inline fun TypeSystemInferenceExtensionContext.isProperTypeForFixation(
+ type: KotlinTypeMarker,
+ isNotFixedTypeVariable: (TypeConstructorMarker) -> Boolean,
+ isProper: (KotlinTypeMarker) -> Boolean
+): Boolean {
+ if (isNotFixedTypeVariable(type.typeConstructor())) 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..952e236 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
}
}
@@ -125,7 +131,7 @@
override fun hashCode() = typeHashCode
- override fun toString() = "$kind($type) from $position"
+ override fun toString() = "$kind($type)"
}
interface VariableWithConstraints {
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..3a9244d 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,25 @@
* @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) {
+ 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 +223,7 @@
// ConstraintSystemBuilder
private fun transactionRegisterVariable(variable: TypeVariableMarker) {
if (state != State.TRANSACTION) return
+ if (variable.freshTypeConstructor() in storage.allTypeVariables) return
typeVariablesTransaction.add(variable)
}
@@ -302,13 +305,24 @@
}
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
+ 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 +331,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 +348,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 +412,13 @@
it
if (typeToCheck == null) return@contains false
- if (typeVariablesThatAreNotCountedAsProperTypes != null) {
- return@contains typeVariablesThatAreNotCountedAsProperTypes!!.contains(typeToCheck.typeConstructor())
+ if (typeVariablesThatAreCountedAsProperTypes != null) {
+ if (typeVariablesThatAreCountedAsProperTypes!!.contains(typeToCheck.typeConstructor())) return@contains false
}
- storage.allTypeVariables.containsKey(typeToCheck.typeConstructor())
+ if (!storage.allTypeVariables.containsKey(typeToCheck.typeConstructor())) return@contains false
+
+ return@contains true
}
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..cb064eb 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
@@ -76,10 +79,11 @@
)
}
}
- ConstraintSystemCompletionMode.PARTIAL -> {
+ ConstraintSystemCompletionMode.PARTIAL, ConstraintSystemCompletionMode.PARTIAL_PCLA -> {
candidate.runCompletion(completionMode, diagnosticHolder, resolutionCallbacks)
candidate.asCallResolutionResult(completionMode, diagnosticHolder)
}
+ ConstraintSystemCompletionMode.PARTIAL_PCLA -> error("PCLA might be only run for K2")
ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA -> throw IllegalStateException("Should not be here")
}
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java
index 5fe771c..9294e7d 100644
--- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java
+++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java
@@ -17374,6 +17374,12 @@
}
@Test
+ @TestMetadata("lastLambdaStatementWithFixedVariables.kt")
+ public void testLastLambdaStatementWithFixedVariables() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/inference/builderInference/lastLambdaStatementWithFixedVariables.kt");
+ }
+
+ @Test
@TestMetadata("manyArgsDifferentYields.kt")
public void testManyArgsDifferentYields() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/builderInference/manyArgsDifferentYields.kt");
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..d403c0601 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,10 @@
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 org.jetbrains.kotlin.utils.addIfNotNull
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@@ -279,8 +282,12 @@
}
}
- fun KotlinTypeMarker.extractTypeVariables(): Set<TypeVariableTypeConstructorMarker> =
+ fun KotlinTypeMarker.extractTypeVariables(includeTypeItself: Boolean = false): Set<TypeVariableTypeConstructorMarker> =
buildSet {
+ // TODO: Drop parameter
+ if (includeTypeItself) {
+ addIfNotNull(typeConstructor() as? TypeVariableTypeConstructorMarker)
+ }
extractTypeOf(this) { it as? TypeVariableTypeConstructorMarker }
}
@@ -551,6 +558,8 @@
fun typeSubstitutorByTypeConstructor(map: Map<TypeConstructorMarker, KotlinTypeMarker>): TypeSubstitutorMarker
fun createEmptySubstitutor(): TypeSubstitutorMarker
+ fun createSubstitutionFromSubtypingStubTypesToTypeVariables(): TypeSubstitutorMarker
+
/**
* @returns substituted type or [type] if there were no substitution
*/
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..d715d21 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,10 @@
errorSupportedOnlyInTypeInference()
}
+ 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)