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