~ WIP
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt index 460eb3f..83150b7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt
@@ -30,7 +30,7 @@ import org.jetbrains.kotlin.resolve.calls.tower.isSuccess class Candidate( - override val symbol: FirBasedSymbol<*>, + override var symbol: FirBasedSymbol<*>, // Here we may have an ExpressionReceiverValue // - in case a use-site receiver is explicit // - in some cases with static entities, no matter is a use-site receiver explicit or not
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt index 4081787..10a3257 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt
@@ -23,6 +23,7 @@ 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.resolve.transformers.body.resolve.BodyResolveContext import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType @@ -33,15 +34,16 @@ import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.fir.visitors.transformSingle -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.calls.inference.addEqualityConstraintIfCompatible 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.types.TypeApproximatorConfiguration import org.jetbrains.kotlin.types.model.StubTypeMarker +import org.jetbrains.kotlin.types.model.TypeConstructorMarker import org.jetbrains.kotlin.types.model.TypeVariableMarker import org.jetbrains.kotlin.types.model.safeSubstitute +import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.runIf class FirCallCompleter( @@ -83,6 +85,30 @@ call.replaceLambdaArgumentInvocationKinds(session) } + if (inferenceSession.skipCompletion(call, resolutionMode, completionMode)) { + if (candidate.callInfo.name == OperatorNameConventions.PROVIDE_DELEGATE && resolutionMode != ResolutionMode.ContextDependentDelegate) { + val inferenceSession = inferenceSession as FirDelegatedPropertyInferenceSession + + inferenceSession.currentConstraintSystem.clearCaches() + val innerSet = candidate.freshVariables.mapTo(mutableSetOf()) { it.typeConstructor } + @Suppress("UNCHECKED_CAST") + inferenceSession.currentConstraintSystem.relevantTypeVariables = innerSet as MutableSet<TypeConstructorMarker> + + runCompletionForCall(candidate, ConstraintSystemCompletionMode.FULL, call, initialType, analyzer) + + inferenceSession.currentConstraintSystem.clearCaches() + inferenceSession.currentConstraintSystem.relevantTypeVariables = null + } else { + runCompletionForCall(candidate, ConstraintSystemCompletionMode.PARTIAL, call, initialType, analyzer) + } + // Running completion at least partially is required to filter out some of the candidates of subsequent calls, + // thus avoiding resolution ambiguity for them. + // Can't use `completionCall` in case it's FULL, because we can't force getValue() completion until BI lambdas in delegate expression are completed + // But those lambdas cannot be found at getValeu() calls as they don't belong there + + return call + } + return when (completionMode) { ConstraintSystemCompletionMode.FULL -> { if (inferenceSession.shouldRunCompletion(call)) { @@ -91,7 +117,7 @@ .buildAbstractResultingSubstitutor(session.typeContext) as ConeSubstitutor val completedCall = call.transformSingle( FirCallCompletionResultsWriterTransformer( - session, finalSubstitutor, + session, components.scopeSession, finalSubstitutor, components.returnTypeCalculator, session.typeApproximator, components.dataFlowAnalyzer, @@ -111,12 +137,6 @@ ConstraintSystemCompletionMode.PARTIAL -> { runCompletionForCall(candidate, completionMode, call, initialType, analyzer) - // Add top-level delegate call as partially resolved to inference session - if (resolutionMode is ResolutionMode.ContextDependentDelegate) { - require(inferenceSession is FirDelegatedPropertyInferenceSession) - inferenceSession.addPartiallyResolvedCall(call) - } - call } @@ -210,7 +230,7 @@ mode: FirCallCompletionResultsWriterTransformer.Mode = FirCallCompletionResultsWriterTransformer.Mode.Normal ): FirCallCompletionResultsWriterTransformer { return FirCallCompletionResultsWriterTransformer( - session, substitutor, components.returnTypeCalculator, + session, components.scopeSession, substitutor, components.returnTypeCalculator, session.typeApproximator, components.dataFlowAnalyzer, components.integerLiteralAndOperatorApproximationTransformer, @@ -325,16 +345,23 @@ ) } - transformer.context.withAnonymousFunctionTowerDataContext(lambdaArgument.symbol) { + val context: BodyResolveContext = transformer.context + context.withAnonymousFunctionTowerDataContext(lambdaArgument.symbol) { if (builderInferenceSession != null) { - transformer.context.withInferenceSession(builderInferenceSession) { + context.withInferenceSession(builderInferenceSession) { lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef)) } } else { - lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef)) + context.withInferenceSession( + if (context.inferenceSession is FirDelegatedPropertyInferenceSession) { + FirInferenceSession.DEFAULT + } else context.inferenceSession + ) { + lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef)) + } } } - transformer.context.dropContextForAnonymousFunction(lambdaArgument) + context.dropContextForAnonymousFunction(lambdaArgument) val returnArguments = components.dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(lambdaArgument)
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt index a40d858..d4a4b2a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt
@@ -6,22 +6,28 @@ 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.resolve.calls.* -import org.jetbrains.kotlin.fir.resolve.inference.model.ConeFixVariableConstraintPosition -import org.jetbrains.kotlin.fir.resolve.substitution.* +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.substitution.ChainedSubstitutor +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder -import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor 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.model.BuilderInferencePosition import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl -import org.jetbrains.kotlin.resolve.calls.inference.registerTypeVariableIfNotPresent -import org.jetbrains.kotlin.types.model.* +import org.jetbrains.kotlin.types.model.TypeConstructorMarker +import org.jetbrains.kotlin.types.model.TypeVariableMarker +import org.jetbrains.kotlin.util.OperatorNameConventions class FirDelegatedPropertyInferenceSession( val property: FirProperty, @@ -29,141 +35,74 @@ private val postponedArgumentsAnalyzer: PostponedArgumentsAnalyzer, ) : FirInferenceSessionForChainedResolve(resolutionContext) { - private val currentConstraintSystem = components.session.inferenceComponents.createConstraintSystem() - override val currentConstraintStorage: ConstraintStorage get() = currentConstraintSystem.currentStorage() + var currentConstraintSystem = components.session.inferenceComponents.createConstraintSystem() + override val currentConstraintStorage: ConstraintStorage + get() { + return currentConstraintSystem.currentStorage() + } private val unitType: ConeClassLikeType = components.session.builtinTypes.unitType.type - private lateinit var resultingConstraintSystem: NewConstraintSystem - - private fun ConeKotlinType.containsStubType(): Boolean { - return this.contains { - it is ConeStubTypeForChainInference - } - } - - private fun integrateResolvedCall(storage: ConstraintStorage) { - registerSyntheticVariables(storage) - val stubToTypeVariableSubstitutor = createToSyntheticTypeVariableSubstitutor() - integrateConstraints( - currentConstraintSystem, - storage, - stubToTypeVariableSubstitutor, - false - ) - } override fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement { - partiallyResolvedCalls += call to candidate - if (candidate.isSuccessful) { - integrateResolvedCall(candidate.system.asReadOnlyStorage()) - } + // TODO } override fun <T> addPartiallyResolvedCall(call: T) where T : FirResolvable, T : FirStatement { - super.addPartiallyResolvedCall(call) - if (call.candidate.isSuccessful) { - integrateResolvedCall(call.candidate.system.currentStorage()) - } + // TODO } - override fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement { - val callee = call.calleeReference as? FirNamedReferenceWithCandidate ?: return true + override fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true - if (callee.candidate.system.hasContradiction) return true + override fun <T> skipCompletion( + call: T, + resolutionMode: ResolutionMode, + completionMode: ConstraintSystemCompletionMode + ): Boolean where T : FirResolvable, T : FirStatement { + if (!call.candidate.isSuccessful && call.isOperatorCallWithName { it == OperatorNameConventions.PROVIDE_DELEGATE }) return false + // Do not run completion for provideDelegate/getValue/setValue because they might affect each other + if (completionMode == ConstraintSystemCompletionMode.FULL && resolutionMode == ResolutionMode.ContextDependentDelegate) return false - val hasStubType = - callee.candidate.chosenExtensionReceiver?.typeRef?.coneType?.containsStubType() ?: false - || callee.candidate.dispatchReceiver?.typeRef?.coneType?.containsStubType() ?: false - - if (!hasStubType) { + if (resolutionMode == ResolutionMode.ContextDependentDelegate || call.isAnyOfDelegateOperators()) { + partiallyResolvedCalls.add(call to call.candidate) + currentConstraintSystem = call.candidate.system return true } - val system = call.candidate.system - - 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 || isSyntheticTypeVariable(variable!!) || - components.callCompleter.completer.variableFixationFinder.isTypeVariableHasProperConstraint(system, it) - } + return false } - private fun FirStatement.hasPostponed(): Boolean { - var result = false - processAllContainingCallCandidates(processBlocks = false) { - result = result || it.hasPostponed() - } - return result + private fun <T> T.isAnyOfDelegateOperators(): Boolean where T : FirResolvable, T : FirStatement = isOperatorCallWithName { + it == OperatorNameConventions.PROVIDE_DELEGATE || it == OperatorNameConventions.GET_VALUE || it == OperatorNameConventions.SET_VALUE } - private fun Candidate.hasPostponed(): Boolean { - return postponedAtoms.any { !it.analyzed } + private fun <T> T.isOperatorCallWithName(predicate: (Name) -> Boolean): Boolean where T : FirResolvable, T : FirStatement { + if (this !is FirFunctionCall) return false + val name = calleeReference.name + if (!predicate(name)) return false + + return origin == FirFunctionCallOrigin.Operator } + override fun inferPostponedVariables( lambda: ResolvedLambdaAtom, constraintSystemBuilder: ConstraintSystemBuilder, completionMode: ConstraintSystemCompletionMode ): Map<ConeTypeVariableTypeConstructor, ConeKotlinType>? = null - private fun createNonFixedTypeToVariableSubstitutor(): NotFixedTypeToVariableSubstitutorForDelegateInference { - val typeContext = components.session.typeContext - - val bindings = mutableMapOf<TypeVariableMarker, ConeKotlinType>() - for ((variable, synthetic) in syntheticTypeVariableByTypeVariable) { - bindings[synthetic] = variable.defaultType(typeContext) as ConeKotlinType - } - - return NotFixedTypeToVariableSubstitutorForDelegateInference(bindings, typeContext) - } - - - /* - * This creates Stub-preserving substitution to synthetic type variables - * Stub(R) => Stub(_R) - * R => _R - */ - private fun createToSyntheticTypeVariableSubstitutor(): ConeSubstitutor { - - val typeContext = components.session.typeContext - val bindings = mutableMapOf<TypeConstructorMarker, ConeKotlinType>() - for ((variable, syntheticVariable) in syntheticTypeVariableByTypeVariable) { - bindings[variable.freshTypeConstructor(typeContext)] = syntheticVariable.defaultType - } - - return typeContext.typeSubstitutorByTypeConstructor(bindings) - } - - override fun hasSyntheticTypeVariables(): Boolean { - return syntheticTypeVariableByTypeVariable.isNotEmpty() - } - - override fun isSyntheticTypeVariable(typeVariable: TypeVariableMarker): Boolean { - return typeVariable in syntheticTypeVariableByTypeVariable.values + override fun createSyntheticStubTypes(system: NewConstraintSystemImpl): Map<TypeConstructorMarker, ConeStubType> { + TODO("Not yet implemented") } override fun fixSyntheticTypeVariableWithNotEnoughInformation( typeVariable: TypeVariableMarker, completionContext: ConstraintSystemCompletionContext ) { - typeVariable as ConeTypeVariable - completionContext.fixVariable( - typeVariable, - ConeStubTypeForSyntheticFixation( - ConeStubTypeConstructor(typeVariable, isTypeVariableInSubtyping = false, isForFixation = true), - ConeNullability.create(typeVariable.defaultType.isMarkedNullable) - ), - ConeFixVariableConstraintPosition(typeVariable) - ) + error("") } fun completeCandidates(): List<FirResolvable> { - val commonSystem = components.session.inferenceComponents.createConstraintSystem() + val commonSystem = currentConstraintSystem val notCompletedCalls = partiallyResolvedCalls.mapNotNull { partiallyResolvedCall -> partiallyResolvedCall.first.takeIf { resolvable -> @@ -171,20 +110,6 @@ } } - val stubToTypeVariableSubstitutor = createNonFixedTypeToVariableSubstitutor() - - for ((call, candidate) in partiallyResolvedCalls) { - if (candidate.isSuccessful) { - integrateConstraints( - commonSystem, - candidate.system.asReadOnlyStorage(), - stubToTypeVariableSubstitutor, - call.candidate() != null - ) - } - } - - resolutionContext.bodyResolveContext.withInferenceSession(DEFAULT) { @Suppress("UNCHECKED_CAST") components.callCompleter.completer.complete( @@ -218,97 +143,18 @@ } } - resultingConstraintSystem = commonSystem return notCompletedCalls } fun createFinalSubstitutor(): ConeSubstitutor { - val stubTypeSubstitutor = createNonFixedTypeToVariableSubstitutor() - val typeContext = components.session.typeContext - val resultSubstitutor = resultingConstraintSystem.asReadOnlyStorage() + val t = currentConstraintSystem.asReadOnlyStorage() .buildAbstractResultingSubstitutor(typeContext) as ConeSubstitutor - return ChainedSubstitutor(stubTypeSubstitutor, resultSubstitutor) - .replaceStubsAndTypeVariablesToErrors(typeContext, stubTypesByTypeVariable.values.map { it.constructor }) + + return ChainedSubstitutor(t, t) } - private val stubTypesByTypeVariable: MutableMap<ConeTypeVariable, ConeStubType> = mutableMapOf() - private val syntheticTypeVariableByTypeVariable = mutableMapOf<TypeVariableMarker, ConeTypeVariable>() + override fun hasSyntheticTypeVariables(): Boolean = false - private fun registerSyntheticVariables(storage: ConstraintStorage) { - for (variableWithConstraints in storage.notFixedTypeVariables.values) { - val variable = variableWithConstraints.typeVariable as ConeTypeVariable - - val syntheticVariable = syntheticTypeVariableByTypeVariable.getOrPut(variable) { - ConeTypeVariable("_" + variable.typeConstructor.name).also { - currentConstraintSystem.registerVariable(it) - } - } - - stubTypesByTypeVariable.getOrPut(variable) { - ConeStubTypeForChainInference( - syntheticVariable, - ConeNullability.create(syntheticVariable.defaultType.isMarkedNullable) - ) - } - } - } - - override fun createSyntheticStubTypes(system: NewConstraintSystemImpl): Map<TypeConstructorMarker, ConeStubType> { - - val bindings = mutableMapOf<TypeConstructorMarker, ConeStubType>() - registerSyntheticVariables(system.currentStorage()) - for (variable in system.postponedTypeVariables) { - variable as ConeTypeVariable - - bindings[variable.typeConstructor] = stubTypesByTypeVariable[variable]!! - } - - return bindings - } - - override fun registerStubTypes(map: Map<TypeVariableMarker, StubTypeMarker>) { -// @Suppress("UNCHECKED_CAST") -// stubTypesByTypeVariable.putAll(map as Map<ConeTypeVariable, ConeStubType>) - } - - - private fun integrateConstraints( - commonSystem: NewConstraintSystemImpl, - storage: ConstraintStorage, - nonFixedToVariablesSubstitutor: ConeSubstitutor, - shouldIntegrateAllConstraints: Boolean - ) { - if (shouldIntegrateAllConstraints) { - storage.notFixedTypeVariables.values.forEach { - if (isSyntheticTypeVariable(it.typeVariable)) return@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 - - for (initialConstraint in storage.initialConstraints) { - integrateConstraintToSystem( - commonSystem, initialConstraint, callSubstitutor, nonFixedToVariablesSubstitutor, storage.fixedTypeVariables - ) - } - - if (shouldIntegrateAllConstraints) { - for ((variableConstructor, type) in storage.fixedTypeVariables) { - val typeVariable = storage.allTypeVariables.getValue(variableConstructor) - if (isSyntheticTypeVariable(typeVariable)) continue - - commonSystem.registerTypeVariableIfNotPresent(typeVariable) - commonSystem.addEqualityConstraint((typeVariable as ConeTypeVariable).defaultType, type, BuilderInferencePosition) - } - } - } + override fun isSyntheticTypeVariable(typeVariable: TypeVariableMarker): Boolean = false }
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt index 3e7f83d..ccbbfd2 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt
@@ -7,6 +7,7 @@ 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.ConeStubType @@ -26,6 +27,11 @@ } abstract fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement + open fun <T> skipCompletion( + call: T, + resolutionMode: ResolutionMode, + completionMode: ConstraintSystemCompletionMode + ): Boolean where T : FirResolvable, T : FirStatement = false abstract val currentConstraintStorage: ConstraintStorage abstract fun <T> addPartiallyResolvedCall(call: T) where T : FirResolvable, T : FirStatement
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt index aa90ddd..4e2389f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt
@@ -30,12 +30,16 @@ import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeTypeParameterInQualifiedAccess import org.jetbrains.kotlin.fir.resolve.inference.ResolvedLambdaAtom import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor +import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.* +import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator import org.jetbrains.kotlin.fir.scopes.impl.ConvertibleIntegerOperators.binaryOperatorsWithSignedArgument +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.FirConstructorSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.types.* @@ -58,6 +62,7 @@ class FirCallCompletionResultsWriterTransformer( override val session: FirSession, + private val scopeSession: ScopeSession, private val finalSubstitutor: ConeSubstitutor, private val typeCalculator: ReturnTypeCalculator, private val typeApproximator: ConeTypeApproximator, @@ -101,6 +106,9 @@ qualifiedAccessExpression: T, calleeReference: FirNamedReferenceWithCandidate, ): T { val subCandidate = calleeReference.candidate + + subCandidate.refineSubstitutedSymbol() + val declaration = subCandidate.symbol.fir val typeArguments = computeTypeArguments(qualifiedAccessExpression, subCandidate) val typeRef = if (declaration is FirCallableDeclaration) { @@ -134,6 +142,12 @@ } } + if (mode == Mode.DelegatedPropertyCompletion) { + // Update type for `$delegateField` in `$$delegateField.get/setValue()` calls inside accessors + val typeUpdater = TypeUpdaterForDelegateArguments() + qualifiedAccessExpression.transformExplicitReceiver(typeUpdater, null) + } + var dispatchReceiver = subCandidate.dispatchReceiverExpression() var extensionReceiver = subCandidate.chosenExtensionReceiverExpression() if (!declaration.isWrappedIntegerOperator()) { @@ -169,18 +183,58 @@ return qualifiedAccessExpression } + private fun Candidate.refineSubstitutedSymbol() { + val updatedSymbol = symbol.refineSubstitutedSymbol() ?: return + val oldSymbol = symbol + symbol = updatedSymbol + + if (updatedSymbol !is FirFunctionSymbol) return + require(oldSymbol is FirFunctionSymbol) + + val oldArgumentMapping = argumentMapping ?: return + val oldValueParametersToNewMap = oldSymbol.valueParameterSymbols.zip(updatedSymbol.valueParameterSymbols).toMap() + + argumentMapping = oldArgumentMapping.mapValuesTo(linkedMapOf()) { + oldValueParametersToNewMap[it.value.symbol]!!.fir + } + + substitutor = substitutorByMap( + updatedSymbol.typeParameterSymbols.zip(freshVariables).associate { (typeParameter, typeVariable) -> + typeParameter to typeVariable.defaultType + }, + session, + ) + } + + private fun FirBasedSymbol<*>.refineSubstitutedSymbol(): FirBasedSymbol<*>? { + val fir = fir + if (fir !is FirCallableDeclaration) return null + + val dispatchReceiverType = fir.dispatchReceiverType ?: return null + val updatedDispatchReceiverType = finalSubstitutor.substituteOrNull(dispatchReceiverType) ?: return null + + val scope = + updatedDispatchReceiverType.scope( + session, + scopeSession, + FakeOverrideTypeCalculator.DoNothing, + FirResolvePhase.STATUS + ) as? FirClassSubstitutionScope ?: return null + + return when (val original = fir.originalForSubstitutionOverride) { + is FirSimpleFunction -> scope.createSubstitutionOverrideFunction(original.symbol) + is FirProperty -> scope.createSubstitutionOverrideProperty(original.symbol) + is FirConstructor -> scope.createSubstitutionOverrideConstructor(original.symbol) + else -> error("!!!") + } + } + override fun transformQualifiedAccessExpression( qualifiedAccessExpression: FirQualifiedAccessExpression, data: ExpectedArgumentType?, ): FirStatement { val calleeReference = qualifiedAccessExpression.calleeReference as? FirNamedReferenceWithCandidate - ?: return run { - if (mode == Mode.DelegatedPropertyCompletion) { - val typeUpdater = TypeUpdaterForDelegateArguments() - qualifiedAccessExpression.transformSingle(typeUpdater, null) - } - qualifiedAccessExpression - } + ?: return qualifiedAccessExpression val result = prepareQualifiedTransform(qualifiedAccessExpression, calleeReference) val typeRef = result.typeRef as FirResolvedTypeRef val subCandidate = calleeReference.candidate @@ -190,11 +244,6 @@ result.replaceTypeRef(resultType) session.lookupTracker?.recordTypeResolveAsLookup(resultType, qualifiedAccessExpression.source, context.file.source) - if (mode == Mode.DelegatedPropertyCompletion) { - val typeUpdater = TypeUpdaterForDelegateArguments() - result.transformExplicitReceiver(typeUpdater, null) - } - return result } @@ -242,12 +291,6 @@ result.replaceTypeRef(resultType) session.lookupTracker?.recordTypeResolveAsLookup(resultType, functionCall.source, context.file.source) - if (mode == Mode.DelegatedPropertyCompletion) { - val typeUpdater = TypeUpdaterForDelegateArguments() - result.argumentList.transformArguments(typeUpdater, null) - result.transformExplicitReceiver(typeUpdater, null) - } - if (enableArrayOfCallTransformation) { return arrayOfCallTransformer.transformFunctionCall(result, session) }
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt index b23fb7b..afe9b87 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt
@@ -35,7 +35,8 @@ import org.jetbrains.kotlin.fir.resolve.inference.FirStubTypeTransformer import org.jetbrains.kotlin.fir.resolve.inference.ResolvedLambdaAtom import org.jetbrains.kotlin.fir.resolve.inference.extractLambdaInfoFromFunctionType -import org.jetbrains.kotlin.fir.resolve.substitution.createTypeSubstitutorByTypeConstructor +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 @@ -47,6 +48,7 @@ import org.jetbrains.kotlin.fir.types.impl.FirImplicitTypeRefImplWithoutSource import org.jetbrains.kotlin.fir.visitors.FirTransformer import org.jetbrains.kotlin.fir.visitors.transformSingle +import org.jetbrains.kotlin.resolve.calls.inference.buildCurrentSubstitutor open class FirDeclarationsResolveTransformer( transformer: FirAbstractBodyResolveTransformerDispatcher @@ -288,6 +290,7 @@ val finalSubstitutor = createFinalSubstitutor() + // TODO: Replace just property/accessors return types instead val stubTypeCompletionResultsWriter = FirStubTypeTransformer(finalSubstitutor) property.transformSingle(stubTypeCompletionResultsWriter, null) property.replaceReturnTypeRef( @@ -334,18 +337,10 @@ if (delegateExpression is FirResolvable) { val calleeReference = delegateExpression.calleeReference if (calleeReference is FirNamedReferenceWithCandidate) { - val system = calleeReference.candidate.system - system.notFixedTypeVariables.forEach { - system.markPostponedVariable(it.value.typeVariable) - } - val typeVariableTypeToStubType = context.inferenceSession.createSyntheticStubTypes(system) - - val substitutor = createTypeSubstitutorByTypeConstructor( - typeVariableTypeToStubType, session.typeContext, approximateIntegerLiterals = true - ) val delegateExpressionTypeRef = delegateExpression.typeRef - val stubTypeSubstituted = substitutor.substituteOrNull(delegateExpressionTypeRef.coneType) - delegateExpression.replaceTypeRef(delegateExpressionTypeRef.withReplacedConeType(stubTypeSubstituted)) + val returnTypeSubstitutedWithTypeVariables = + calleeReference.candidate.substitutor.substituteOrNull(delegateExpressionTypeRef.coneType) + delegateExpression.replaceTypeRef(delegateExpressionTypeRef.withReplacedConeType(returnTypeSubstitutedWithTypeVariables)) } } @@ -356,28 +351,23 @@ // 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.ContextIndependent, provideDelegate = true + provideDelegateCall, ResolutionMode.ContextIndependent, skipExplicitReceiverTransformation = true ) // If we got successful candidate for provideDelegate, let's select it val provideDelegateCandidate = provideDelegateCall.candidate() if (provideDelegateCandidate != null && provideDelegateCandidate.isSuccessful) { - val system = provideDelegateCandidate.system - system.notFixedTypeVariables.forEach { - system.markPostponedVariable(it.value.typeVariable) - } - val typeVariableTypeToStubType = context.inferenceSession.createSyntheticStubTypes(system) - val substitutor = createTypeSubstitutorByTypeConstructor( - typeVariableTypeToStubType, session.typeContext, approximateIntegerLiterals = true + val substitutor = ChainedSubstitutor( + provideDelegateCandidate.substitutor, + context.inferenceSession.currentConstraintStorage.buildCurrentSubstitutor( + session.typeContext, emptyMap() + ) as ConeSubstitutor ) - val stubTypeSubstituted = substitutor.substituteOrSelf( - provideDelegateCandidate.substitutor.substituteOrSelf( - components.typeFromCallee(provideDelegateCall).type - ) - ) + val toTypeVariableSubstituted = + substitutor.substituteOrSelf(components.typeFromCallee(provideDelegateCall).type) - provideDelegateCall.replaceTypeRef(provideDelegateCall.typeRef.resolvedTypeFromPrototype(stubTypeSubstituted)) + provideDelegateCall.replaceTypeRef(provideDelegateCall.typeRef.resolvedTypeFromPrototype(toTypeVariableSubstituted)) return provideDelegateCall }
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index a7f640b..ca9c7b7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt
@@ -29,6 +29,8 @@ 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.FirDelegatedPropertyInferenceSession +import org.jetbrains.kotlin.fir.resolve.inference.FirInferenceSession import org.jetbrains.kotlin.fir.resolve.inference.FirStubInferenceSession import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.transformers.replaceLambdaArgumentInvocationKinds @@ -393,12 +395,14 @@ } override fun transformFunctionCall(functionCall: FirFunctionCall, data: ResolutionMode): FirStatement = - transformFunctionCallInternal(functionCall, data, provideDelegate = false) + transformFunctionCallInternal(functionCall, data, skipExplicitReceiverTransformation = false) internal fun transformFunctionCallInternal( functionCall: FirFunctionCall, data: ResolutionMode, - provideDelegate: Boolean, + // Currently, it's only `true` for provideDelegate calls, because delegateExpression is already resolved as that stage. + // See also FirDeclarationsResolveTransformer.transformWrappedDelegateExpression + skipExplicitReceiverTransformation: Boolean, ): FirStatement = whileAnalysing(session, functionCall) { val calleeReference = functionCall.calleeReference @@ -420,19 +424,21 @@ functionCall.transformAnnotations(transformer, data) functionCall.replaceLambdaArgumentInvocationKinds(session) functionCall.transformTypeArguments(transformer, ResolutionMode.ContextIndependent) - val initialExplicitReceiver = functionCall.explicitReceiver - val withTransformedArguments = if (!resolvingAugmentedAssignment) { - dataFlowAnalyzer.enterCallArguments(functionCall, functionCall.arguments) - // In provideDelegate mode the explicitReceiver is already resolved - // E.g. we have val some by someDelegate - // At 1st stage of delegate inference we resolve someDelegate itself, - // at 2nd stage in provideDelegate mode we are trying to resolve someDelegate.provideDelegate(), - // and 'someDelegate' explicit receiver is resolved at 1st stage - // See also FirDeclarationsResolveTransformer.transformWrappedDelegateExpression - val withResolvedExplicitReceiver = if (provideDelegate) functionCall else transformExplicitReceiver(functionCall) + + val initialExplicitReceiver = functionCall.explicitReceiver + val withTransformedArguments = if (!resolvingAugmentedAssignment) { + dataFlowAnalyzer.enterCallArguments(functionCall, functionCall.arguments) + val withResolvedExplicitReceiver = + if (skipExplicitReceiverTransformation) functionCall else transformExplicitReceiver(functionCall) withResolvedExplicitReceiver.also { - it.replaceArgumentList(it.argumentList.transform(this, ResolutionMode.ContextDependent)) - dataFlowAnalyzer.exitCallArguments() + context.withInferenceSession( + if (context.inferenceSession is FirDelegatedPropertyInferenceSession) { + FirInferenceSession.DEFAULT + } else context.inferenceSession + ) { + it.replaceArgumentList(it.argumentList.transform(this, ResolutionMode.ContextDependent)) + dataFlowAnalyzer.exitCallArguments() + } } } else { functionCall @@ -1235,18 +1241,20 @@ annotationCall: FirAnnotationCall, data: ResolutionMode ): FirStatement = whileAnalysing(session, annotationCall) { - if (annotationCall.resolved) return annotationCall - annotationCall.transformAnnotationTypeRef(transformer, ResolutionMode.ContextIndependent) - annotationCall.replaceAnnotationResolvePhase(FirAnnotationResolvePhase.Types) - return context.forAnnotation { - withFirArrayOfCallTransformer { - dataFlowAnalyzer.enterAnnotation() - val result = callResolver.resolveAnnotationCall(annotationCall) - dataFlowAnalyzer.exitAnnotation() - if (result == null) return annotationCall - callCompleter.completeCall(result, ResolutionMode.ContextIndependent) - (result.argumentList as FirResolvedArgumentList).let { annotationCall.replaceArgumentMapping((it).toAnnotationArgumentMapping()) } - annotationCall + context.withInferenceSession(FirInferenceSession.DEFAULT) { + if (annotationCall.resolved) return annotationCall + annotationCall.transformAnnotationTypeRef(transformer, ResolutionMode.ContextIndependent) + annotationCall.replaceAnnotationResolvePhase(FirAnnotationResolvePhase.Types) + context.forAnnotation { + withFirArrayOfCallTransformer { + dataFlowAnalyzer.enterAnnotation() + val result = callResolver.resolveAnnotationCall(annotationCall) + dataFlowAnalyzer.exitAnnotation() + if (result == null) return annotationCall + callCompleter.completeCall(result, ResolutionMode.ContextIndependent) + (result.argumentList as FirResolvedArgumentList).let { annotationCall.replaceArgumentMapping((it).toAnnotationArgumentMapping()) } + annotationCall + } } } }
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt index 4f725e3..199a7e9 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt
@@ -27,6 +27,7 @@ val fixedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker> val postponedTypeVariables: List<TypeVariableMarker> val constraintsFromAllForkPoints: MutableList<Pair<IncorporationConstraintPosition, ForkPointData>> + val relevantTypeVariables: Set<TypeConstructorMarker>? fun isReified(variable: TypeVariableMarker): Boolean } @@ -182,7 +183,10 @@ && !c.isNullabilityConstraint private fun Context.isProperType(type: KotlinTypeMarker): Boolean = - isProperTypeForFixation(type) { t -> !t.contains { notFixedTypeVariables.containsKey(it.typeConstructor()) } } + isProperTypeForFixation(type) { t -> !t.contains { isNotFixedRelevantVariable(it) } } + + private fun Context.isNotFixedRelevantVariable(it: KotlinTypeMarker) = + (relevantTypeVariables == null || relevantTypeVariables!!.contains(it.typeConstructor())) && notFixedTypeVariables.containsKey(it.typeConstructor()) private fun Context.isReified(variable: TypeConstructorMarker): Boolean = notFixedTypeVariables[variable]?.typeVariable?.let { isReified(it) } ?: false
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt index a47f80a..60237cd 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt
@@ -44,8 +44,15 @@ private val intersectionTypesCache: MutableMap<Collection<KotlinTypeMarker>, EmptyIntersectionTypeInfo?> = mutableMapOf() private var couldBeResolvedWithUnrestrictedBuilderInference: Boolean = false + override var relevantTypeVariables: MutableSet<TypeConstructorMarker>? = null + override var atCompletionState: Boolean = false + fun clearCaches() { + properTypesCache.clear() + notProperTypesCache.clear() + } + private enum class State { BUILDING, TRANSACTION, @@ -317,6 +324,9 @@ it if (typeToCheck == null) return@contains false + if (relevantTypeVariables != null) { + return@contains relevantTypeVariables!!.contains(typeToCheck.typeConstructor()) + } storage.allTypeVariables.containsKey(typeToCheck.typeConstructor()) }