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