From bb411cbba70963884601067b08debe281b32d3fc Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Sun, 14 Nov 2021 16:13:55 +0300 Subject: [PATCH] FIR: Try to implement delegate inference by using stub types chain --- .../AbstractManyCandidatesInferenceSession.kt | 15 +- .../inference/ConstraintSystemCompleter.kt | 10 +- .../inference/FirBuilderInferenceSession.kt | 30 +- .../fir/resolve/inference/FirCallCompleter.kt | 11 +- .../FirDelegatedPropertyInferenceSession.kt | 332 ++++++++++++++---- .../resolve/inference/FirInferenceSession.kt | 22 +- .../body/resolve/BodyResolveContext.kt | 2 - .../FirDeclarationsResolveTransformer.kt | 85 +++-- 8 files changed, 365 insertions(+), 142 deletions(-) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/AbstractManyCandidatesInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/AbstractManyCandidatesInferenceSession.kt index 974c5c064b4..1369b867472 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/AbstractManyCandidatesInferenceSession.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/AbstractManyCandidatesInferenceSession.kt @@ -11,7 +11,8 @@ import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext import org.jetbrains.kotlin.fir.resolve.calls.candidate -import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage +import org.jetbrains.kotlin.types.model.StubTypeMarker +import org.jetbrains.kotlin.types.model.TypeVariableMarker abstract class AbstractManyCandidatesInferenceSession( protected val resolutionContext: ResolutionContext @@ -23,13 +24,6 @@ abstract class AbstractManyCandidatesInferenceSession( protected val components: BodyResolveComponents get() = resolutionContext.bodyResolveComponents - override val currentConstraintSystem: ConstraintStorage - get() = partiallyResolvedCalls.lastOrNull() - ?.second - ?.system - ?.asReadOnlyStorage() - ?: ConstraintStorage.Empty - override fun addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement { // do nothing } @@ -42,9 +36,7 @@ abstract class AbstractManyCandidatesInferenceSession( errorCalls += call } - final override fun callCompleted(call: T): Boolean where T : FirResolvable, T : FirStatement { - return !completedCalls.add(call) - } + override fun registerStubTypes(map: Map) {} protected val FirResolvable.candidate: Candidate get() = candidate()!! @@ -54,4 +46,5 @@ abstract class AbstractManyCandidatesInferenceSession( partiallyResolvedCalls.clear() completedCalls.clear() } + } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt index 94be84cc421..6802a13dcbb 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext import org.jetbrains.kotlin.fir.resolve.inference.model.ConeFixVariableConstraintPosition +import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext import org.jetbrains.kotlin.fir.returnExpressions import org.jetbrains.kotlin.fir.types.ConeClassErrorType import org.jetbrains.kotlin.fir.types.ConeKotlinType @@ -33,7 +34,7 @@ import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.cast import org.jetbrains.kotlin.utils.addToStdlib.safeAs -class ConstraintSystemCompleter(private val components: BodyResolveComponents) { +class ConstraintSystemCompleter(private val components: BodyResolveComponents, private val context: BodyResolveContext) { private val inferenceComponents = components.session.inferenceComponents val variableFixationFinder = inferenceComponents.variableFixationFinder private val postponedArgumentsInputTypesResolver = inferenceComponents.postponedArgumentInputTypesResolver @@ -50,7 +51,7 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents) { val topLevelTypeVariables = candidateReturnType.extractTypeVariables() completion@ while (true) { - val postponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms) + val postponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms) // TODO: This is very slow if (completionMode == ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA && hasLambdaToAnalyze(postponedArguments)) return @@ -225,7 +226,7 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents) { val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) - if (variableForFixation.hasProperConstraint) { + if (variableForFixation.hasProperConstraint || context.inferenceSession.isSyntheticTypeVariable(variableWithConstraints.typeVariable)) { fixVariable(asConstraintSystemCompletionContext(), topLevelType, variableWithConstraints, postponedArguments) return true } else { @@ -380,6 +381,8 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents) { topLevel.collectAllTypeVariables() } + result.addAll(c.notFixedTypeVariables.filter { context.inferenceSession.isSyntheticTypeVariable(it.value.typeVariable) }.keys.asIterable().reversed()) + require(result.size == c.notFixedTypeVariables.size) { val notFoundTypeVariables = c.notFixedTypeVariables.keys.toMutableSet().apply { removeAll(result) } "Not all type variables found: $notFoundTypeVariables" @@ -421,7 +424,6 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents) { fun FirStatement.processAllContainingCallCandidates(processBlocks: Boolean, processor: (Candidate) -> Unit) { when (this) { is FirFunctionCall -> { - explicitReceiver?.processAllContainingCallCandidates(processBlocks, processor) processCandidateIfApplicable(processor, processBlocks) this.arguments.forEach { it.processAllContainingCallCandidates(processBlocks, processor) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt index 5e24bc3ba84..f5461eb2fb9 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl import org.jetbrains.kotlin.resolve.descriptorUtil.BUILDER_INFERENCE_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.types.model.TypeConstructorMarker +import org.jetbrains.kotlin.types.model.TypeVariableMarker class FirBuilderInferenceSession( private val lambda: FirAnonymousFunction, @@ -35,6 +36,13 @@ class FirBuilderInferenceSession( ) : AbstractManyCandidatesInferenceSession(resolutionContext) { private val commonCalls: MutableList> = mutableListOf() + override val currentConstraintSystem: ConstraintStorage + get() = ConstraintStorage.Empty + + override fun isSyntheticTypeVariable(typeVariable: TypeVariableMarker): Boolean { + return false + } + override fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement { val candidate = call.candidate val system = candidate.system @@ -88,10 +96,6 @@ class FirBuilderInferenceSession( commonCalls += call to candidate } - override fun writeOnlyStubs(call: T): Boolean where T : FirResolvable, T : FirStatement { - return !skipCall(call) - } - @Suppress("UNUSED_PARAMETER") private fun skipCall(call: T): Boolean where T : FirResolvable, T : FirStatement { // TODO: what is FIR analog? @@ -101,13 +105,6 @@ class FirBuilderInferenceSession( return false } - override val currentConstraintSystem: ConstraintStorage - get() = ConstraintStorage.Empty - - override fun shouldCompleteResolvedSubAtomsOf(call: T): Boolean where T : FirResolvable, T : FirStatement { - return true - } - override fun inferPostponedVariables( lambda: ResolvedLambdaAtom, initialStorage: ConstraintStorage, @@ -137,6 +134,8 @@ class FirBuilderInferenceSession( return commonSystem.fixedTypeVariables as Map } + override fun createSyntheticStubTypes(system: NewConstraintSystemImpl): Map = emptyMap() + private fun buildCommonSystem(initialStorage: ConstraintStorage): Pair { val commonSystem = components.session.inferenceComponents.createConstraintSystem() val nonFixedToVariablesSubstitutor = createNonFixedTypeToVariableSubstitutor() @@ -190,13 +189,16 @@ class FirBuilderInferenceSession( * * while substitutor from parameter map non-fixed types to the original type variable * */ - val callSubstitutor = storage.buildAbstractResultingSubstitutor(commonSystem, transformTypeVariablesToErrorTypes = false) as ConeSubstitutor + val callSubstitutor = + storage.buildAbstractResultingSubstitutor(commonSystem, transformTypeVariablesToErrorTypes = false) as ConeSubstitutor var introducedConstraint = false for (initialConstraint in storage.initialConstraints) { - val lower = nonFixedToVariablesSubstitutor.substituteOrSelf(callSubstitutor.substituteOrSelf(initialConstraint.a as ConeKotlinType)) // TODO: SUB - val upper = nonFixedToVariablesSubstitutor.substituteOrSelf(callSubstitutor.substituteOrSelf(initialConstraint.b as ConeKotlinType)) // TODO: SUB + val lower = + nonFixedToVariablesSubstitutor.substituteOrSelf(callSubstitutor.substituteOrSelf(initialConstraint.a as ConeKotlinType)) // TODO: SUB + val upper = + nonFixedToVariablesSubstitutor.substituteOrSelf(callSubstitutor.substituteOrSelf(initialConstraint.b as ConeKotlinType)) // TODO: SUB if (commonSystem.isProperType(lower) && commonSystem.isProperType(upper)) continue 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 7420159972b..5d89b001143 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 @@ -45,10 +45,12 @@ class FirCallCompleter( private val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents ) { private val session = components.session - val completer = ConstraintSystemCompleter(components) private val inferenceSession get() = transformer.context.inferenceSession + val completer = ConstraintSystemCompleter(components, transformer.context) + + data class CompletionResult(val result: T, val callCompleted: Boolean) fun completeCall( @@ -57,11 +59,8 @@ class FirCallCompleter( expectedTypeMismatchIsReportedInChecker: Boolean = false, ): CompletionResult where T : FirResolvable, T : FirStatement = completeCall( - call, - expectedTypeRef, - mayBeCoercionToUnitApplied = false, - expectedTypeMismatchIsReportedInChecker, - isFromCast = false, + call, expectedTypeRef, mayBeCoercionToUnitApplied = false, expectedTypeMismatchIsReportedInChecker, isFromCast = false + , shouldEnforceExpectedType = true, ) 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 8b68c7117ae..f0ce41784c7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt @@ -5,49 +5,102 @@ package org.jetbrains.kotlin.fir.resolve.inference -import org.jetbrains.kotlin.fir.declarations.* -import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.expressions.FirResolvable import org.jetbrains.kotlin.fir.expressions.FirStatement -import org.jetbrains.kotlin.fir.references.FirNamedReference -import org.jetbrains.kotlin.fir.resolve.calls.Candidate -import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate -import org.jetbrains.kotlin.fir.resolve.calls.InferenceError -import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext -import org.jetbrains.kotlin.fir.resolve.defaultType +import org.jetbrains.kotlin.fir.resolve.calls.* +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.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.types.* -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.ConstraintSystemCompletionMode -import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage -import org.jetbrains.kotlin.resolve.calls.inference.model.DelegatedPropertyConstraintPosition -import org.jetbrains.kotlin.util.OperatorNameConventions -import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import org.jetbrains.kotlin.resolve.calls.inference.model.* +import org.jetbrains.kotlin.types.model.* class FirDelegatedPropertyInferenceSession( val property: FirProperty, - initialCall: FirExpression, resolutionContext: ResolutionContext, private val postponedArgumentsAnalyzer: PostponedArgumentsAnalyzer, ) : AbstractManyCandidatesInferenceSession(resolutionContext) { - init { - val initialCandidate = (initialCall as? FirResolvable) - ?.calleeReference - ?.safeAs() - ?.candidate - if (initialCandidate != null) { - addPartiallyResolvedCall(initialCall) - } - } - val expectedType: ConeKotlinType? by lazy { property.returnTypeRef.coneTypeSafe() } + override val currentConstraintSystem: ConstraintStorage + get() { +// return ConstraintStorage.Empty + val system = components.session.inferenceComponents.createConstraintSystem() + + val stubToTypeVariableSubstitutor = createToSyntheticTypeVariableSubstitutor() + syntheticTypeVariableByTypeVariable.values.forEach { + system.registerVariable(it) + val stubType = + stubTypeBySyntheticTypeVariable[it]!! + system.addEqualityConstraint(it.defaultType, stubType, SimpleConstraintSystemConstraintPosition) + } + partiallyResolvedCalls.forEach { (_, candidate) -> + integrateConstraints( + system, + candidate.system.asReadOnlyStorage(), + stubToTypeVariableSubstitutor, + false + ) + } + + return system.currentStorage() + } + + private val commonSystem = components.session.inferenceComponents.createConstraintSystem() private val unitType: ConeKotlinType = components.session.builtinTypes.unitType.type private lateinit var resultingConstraintSystem: NewConstraintSystem - override fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = false + private fun ConeKotlinType.containsStubType(): Boolean { + return this.contains { + it is ConeStubTypeForBuilderInference + } + } + + override fun addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement { + partiallyResolvedCalls += call to candidate + } + + override fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement { + val callee = call.calleeReference as? FirNamedReferenceWithCandidate ?: return true + + if (callee.candidate.system.hasContradiction) return true + + val hasStubType = + callee.candidate.extensionReceiverValue?.type?.containsStubType() ?: false + || callee.candidate.dispatchReceiverValue?.type?.containsStubType() ?: false + + if (!hasStubType) { + 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) + } + } + + 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 } + } override fun inferPostponedVariables( lambda: ResolvedLambdaAtom, @@ -55,27 +108,79 @@ class FirDelegatedPropertyInferenceSession( completionMode: ConstraintSystemCompletionMode ): Map? = null - override fun shouldCompleteResolvedSubAtomsOf(call: T): Boolean where T : FirResolvable, T : FirStatement = true + private fun createNonFixedTypeToVariableSubstitutor(): ConeSubstitutor { + val typeContext = components.session.typeContext + + val bindings = mutableMapOf() + for ((variable, stubType) in stubTypesByTypeVariable) { + bindings[stubType.constructor] = variable.defaultType(typeContext) as ConeKotlinType + } + + return object : AbstractConeSubstitutor(typeContext) { + override fun substituteType(type: ConeKotlinType): ConeKotlinType? { + if (type !is ConeStubType) return null + return bindings[type.constructor] + } + } + } + + + /* + * 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() + for ((variable, syntheticVariable) in syntheticTypeVariableByTypeVariable) { + bindings[variable.freshTypeConstructor(typeContext)] = syntheticVariable.defaultType + } + + return typeContext.typeSubstitutorByTypeConstructor(bindings) + } + + + override fun isSyntheticTypeVariable(typeVariable: TypeVariableMarker): Boolean { + return typeVariable in syntheticTypeVariableByTypeVariable.values + } fun completeCandidates(): List { @Suppress("UNCHECKED_CAST") - val resolvedCalls = partiallyResolvedCalls.map { it.first } - val commonSystem = components.session.inferenceComponents.createConstraintSystem().apply { - addOtherSystem(currentConstraintSystem) + val notCompletedCalls = partiallyResolvedCalls.mapNotNull { partiallyResolvedCall -> + partiallyResolvedCall.first.takeIf { resolvable -> + resolvable.candidate() != null + } } - prepareForCompletion(commonSystem, resolvedCalls) + + val stubToTypeVariableSubstitutor = createNonFixedTypeToVariableSubstitutor() + + partiallyResolvedCalls.forEach { (call, candidate) -> + integrateConstraints( + commonSystem, + candidate.system.asReadOnlyStorage(), + stubToTypeVariableSubstitutor, + call.candidate() != null + ) + } + + resolutionContext.bodyResolveContext.withInferenceSession(DEFAULT) { @Suppress("UNCHECKED_CAST") components.callCompleter.completer.complete( commonSystem.asConstraintSystemCompleterContext(), ConstraintSystemCompletionMode.FULL, - resolvedCalls as List, + notCompletedCalls as List, unitType, resolutionContext - ) { + ) { lambdaAtom -> + val containingCandidateForLambda = notCompletedCalls.first { + it.candidate.postponedAtoms.contains(lambdaAtom) + }.candidate postponedArgumentsAnalyzer.analyze( commonSystem.asPostponedArgumentsAnalyzerContext(), - it, - resolvedCalls.first().candidate, + lambdaAtom, + containingCandidateForLambda, ConstraintSystemCompletionMode.FULL, ) } @@ -88,61 +193,136 @@ class FirDelegatedPropertyInferenceSession( } resultingConstraintSystem = commonSystem - return resolvedCalls - } - - private fun prepareForCompletion(commonSystem: NewConstraintSystem, partiallyResolvedCalls: List) { - val csBuilder = commonSystem.getBuilder() - for (call in partiallyResolvedCalls) { - val candidate = call.candidate - when ((call.calleeReference as FirNamedReference).name) { - OperatorNameConventions.GET_VALUE -> candidate.addConstraintsForGetValueMethod(csBuilder) - OperatorNameConventions.SET_VALUE -> candidate.addConstraintsForSetValueMethod(csBuilder) - } - } + return notCompletedCalls } fun createFinalSubstitutor(): ConeSubstitutor { - return resultingConstraintSystem.asReadOnlyStorage() + val stubTypeSubstitutor = createNonFixedTypeToVariableSubstitutor() + + val resultSubstitutor = resultingConstraintSystem.asReadOnlyStorage() .buildAbstractResultingSubstitutor(components.session.typeContext) as ConeSubstitutor + return ChainedSubstitutor(stubTypeSubstitutor, resultSubstitutor) } - private fun Candidate.addConstraintsForGetValueMethod(commonSystem: ConstraintSystemBuilder) { - if (expectedType != null) { - val accessor = symbol.fir as? FirSimpleFunction ?: return - val unsubstitutedReturnType = accessor.returnTypeRef.coneType + val stubTypesByTypeVariable: MutableMap = mutableMapOf() + val stubTypeBySyntheticTypeVariable: MutableMap = mutableMapOf() - val substitutedReturnType = substitutor.substituteOrSelf(unsubstitutedReturnType) - commonSystem.addSubtypeConstraint(substitutedReturnType, expectedType!!, DelegatedPropertyConstraintPosition(callInfo.callSite)) + private val syntheticTypeVariableByTypeVariable = mutableMapOf() + + + override fun createSyntheticStubTypes(system: NewConstraintSystemImpl): Map { + + val bindings = mutableMapOf() + +// val synthToOriginal = syntheticTypeVariableByTypeVariable.entries.associateBy({ it.value }, { it.key }) +// +// for (variable in system.postponedTypeVariables) { +// variable as ConeTypeVariable +// if (isSyntheticTypeVariable(variable)) { +// system.fixVariable( +// variable, +// ConeStubTypeForBuilderInference( +// synthToOriginal[variable]!! as ConeTypeVariable, +// ConeNullability.create(variable.defaultType.isMarkedNullable) +// ), +// ConeFixVariableConstraintPosition(variable) +// ) +// system.unmarkPostponedVariable(variable) +// } +// } + + for (variable in system.postponedTypeVariables) { + variable as ConeTypeVariable + + val syntheticVariable = syntheticTypeVariableByTypeVariable.getOrPut(variable) { + ConeTypeVariable("_" + variable.typeConstructor.name) + } + +// val potentialType = resolutionContext.inferenceComponents.resultTypeResolver.findResultType( +// system, +// system.notFixedTypeVariables[variable.typeConstructor]!!, +// TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN +// ) + + bindings[variable.typeConstructor] = stubTypesByTypeVariable.getOrPut(variable) { + ConeStubTypeForBuilderInference( + syntheticVariable, + ConeNullability.create(syntheticVariable.defaultType.isMarkedNullable) + ).also { + stubTypeBySyntheticTypeVariable[syntheticVariable] = it + } + } } - addConstraintForThis(commonSystem) + return bindings } - private fun Candidate.addConstraintsForSetValueMethod(commonSystem: ConstraintSystemBuilder) { - if (expectedType != null) { - val accessor = symbol.fir as? FirSimpleFunction ?: return - val unsubstitutedParameterType = accessor.valueParameters.getOrNull(2)?.returnTypeRef?.coneType ?: return + override fun registerStubTypes(map: Map) { +// @Suppress("UNCHECKED_CAST") +// stubTypesByTypeVariable.putAll(map as Map) + } - val substitutedReturnType = substitutor.substituteOrSelf(unsubstitutedParameterType) - commonSystem.addSubtypeConstraint(expectedType!!, substitutedReturnType, DelegatedPropertyConstraintPosition(callInfo.callSite)) + + private fun integrateConstraints( + commonSystem: NewConstraintSystemImpl, + storage: ConstraintStorage, + nonFixedToVariablesSubstitutor: ConeSubstitutor, + shouldIntegrateAllConstraints: Boolean + ): Boolean { + if (shouldIntegrateAllConstraints) { + storage.notFixedTypeVariables.values.forEach { + if (isSyntheticTypeVariable(it.typeVariable)) return@forEach + if (it.typeVariable.freshTypeConstructor(commonSystem.typeSystemContext) !in commonSystem.allTypeVariables) { + commonSystem.registerVariable(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) { + val lower = + nonFixedToVariablesSubstitutor.substituteOrSelf(callSubstitutor.substituteOrSelf(initialConstraint.a as ConeKotlinType)) // TODO: SUB + val upper = + nonFixedToVariablesSubstitutor.substituteOrSelf(callSubstitutor.substituteOrSelf(initialConstraint.b as ConeKotlinType)) // TODO: SUB + + if (commonSystem.isProperType(lower) && (lower == upper || commonSystem.isProperType(upper))) continue + + introducedConstraint = true + + when (initialConstraint.constraintKind) { + ConstraintKind.LOWER -> error("LOWER constraint shouldn't be used, please use UPPER") + + ConstraintKind.UPPER -> commonSystem.addSubtypeConstraint(lower, upper, initialConstraint.position) + + ConstraintKind.EQUALITY -> + with(commonSystem) { + addSubtypeConstraint(lower, upper, initialConstraint.position) + addSubtypeConstraint(upper, lower, initialConstraint.position) + } + } } - addConstraintForThis(commonSystem) - } + if (shouldIntegrateAllConstraints) { + for ((variableConstructor, type) in storage.fixedTypeVariables) { + val typeVariable = storage.allTypeVariables.getValue(variableConstructor) + if (isSyntheticTypeVariable(typeVariable)) continue - private fun Candidate.addConstraintForThis(commonSystem: ConstraintSystemBuilder) { - val typeOfThis: ConeKotlinType = property.receiverTypeRef?.coneType - ?: when (val container = components.container) { - is FirRegularClass -> container.defaultType() - is FirAnonymousObject -> container.defaultType() - is FirCallableDeclaration -> container.dispatchReceiverType - else -> null - } ?: components.session.builtinTypes.nullableNothingType.type - val valueParameterForThis = (symbol as? FirFunctionSymbol<*>)?.fir?.valueParameters?.firstOrNull() ?: return - val substitutedType = substitutor.substituteOrSelf(valueParameterForThis.returnTypeRef.coneType) - commonSystem.addSubtypeConstraint(typeOfThis, substitutedType, DelegatedPropertyConstraintPosition(callInfo.callSite)) - } + commonSystem.registerVariable(typeVariable) + commonSystem.addEqualityConstraint((typeVariable as ConeTypeVariable).defaultType, type, BuilderInferencePosition) + introducedConstraint = true + } + } - override fun writeOnlyStubs(call: T): Boolean where T : FirResolvable, T : FirStatement = false + return introducedConstraint + } } 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 da9d03db93f..bdb3a9b09a8 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 @@ -9,9 +9,14 @@ import org.jetbrains.kotlin.fir.expressions.FirResolvable import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.ConeStubType import org.jetbrains.kotlin.fir.types.ConeTypeVariableTypeConstructor 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 +import org.jetbrains.kotlin.types.model.StubTypeMarker +import org.jetbrains.kotlin.types.model.TypeConstructorMarker +import org.jetbrains.kotlin.types.model.TypeVariableMarker abstract class FirInferenceSession { companion object { @@ -25,6 +30,10 @@ abstract class FirInferenceSession { abstract fun addErrorCall(call: T) where T : FirResolvable, T : FirStatement abstract fun addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement + abstract fun registerStubTypes(map: Map) + + abstract fun isSyntheticTypeVariable(typeVariable: TypeVariableMarker): Boolean + abstract fun inferPostponedVariables( lambda: ResolvedLambdaAtom, initialStorage: ConstraintStorage, @@ -32,11 +41,8 @@ abstract class FirInferenceSession { // TODO: diagnostic holder ): Map? - abstract fun writeOnlyStubs(call: T): Boolean where T : FirResolvable, T : FirStatement - abstract fun callCompleted(call: T): Boolean where T : FirResolvable, T : FirStatement - abstract fun shouldCompleteResolvedSubAtomsOf(call: T): Boolean where T : FirResolvable, T : FirStatement - abstract fun clear() + abstract fun createSyntheticStubTypes(system: NewConstraintSystemImpl): Map } abstract class FirStubInferenceSession : FirInferenceSession() { @@ -55,9 +61,11 @@ abstract class FirStubInferenceSession : FirInferenceSession() { completionMode: ConstraintSystemCompletionMode ): Map? = null - override fun writeOnlyStubs(call: T): Boolean where T : FirResolvable, T : FirStatement = false - override fun callCompleted(call: T): Boolean where T : FirResolvable, T : FirStatement = false - override fun shouldCompleteResolvedSubAtomsOf(call: T): Boolean where T : FirResolvable, T : FirStatement = true + override fun registerStubTypes(map: Map) {} + + override fun isSyntheticTypeVariable(typeVariable: TypeVariableMarker): Boolean = false + + override fun createSyntheticStubTypes(system: NewConstraintSystemImpl): Map = emptyMap() override fun clear() { } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt index bc9cca5b0d4..695a01bd1ff 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt @@ -677,14 +677,12 @@ class BodyResolveContext( inline fun forPropertyDelegateAccessors( property: FirProperty, - delegateExpression: FirExpression, resolutionContext: ResolutionContext, callCompleter: FirCallCompleter, f: FirDelegatedPropertyInferenceSession.() -> T ) { val inferenceSession = FirDelegatedPropertyInferenceSession( property, - delegateExpression, resolutionContext, callCompleter.createPostponedArgumentsAnalyzer(resolutionContext) ) 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 8d1e866a371..56cf0e1dcb4 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 @@ -24,20 +24,19 @@ import org.jetbrains.kotlin.fir.expressions.builder.buildReturnExpression import org.jetbrains.kotlin.fir.expressions.builder.buildUnitExpression import org.jetbrains.kotlin.fir.expressions.impl.FirLazyBlock import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference -import org.jetbrains.kotlin.fir.resolve.ResolutionMode -import org.jetbrains.kotlin.fir.resolve.calls.FirErrorReferenceWithCandidate +import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate -import org.jetbrains.kotlin.fir.resolve.constructFunctionalTypeRef +import org.jetbrains.kotlin.fir.resolve.calls.candidate import org.jetbrains.kotlin.fir.resolve.dfa.FirControlFlowGraphReferenceImpl import org.jetbrains.kotlin.fir.resolve.dfa.unwrapSmartcastExpression import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeLocalVariableNoTypeOrInitializer +import org.jetbrains.kotlin.fir.resolve.inference.FirStubTypeTransformer import org.jetbrains.kotlin.fir.resolve.inference.ResolvedLambdaAtom import org.jetbrains.kotlin.fir.resolve.inference.extractLambdaInfoFromFunctionalType import org.jetbrains.kotlin.fir.types.isSuspendFunctionType -import org.jetbrains.kotlin.fir.resolve.mode import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor +import org.jetbrains.kotlin.fir.resolve.substitution.createTypeSubstitutorByTypeConstructor import org.jetbrains.kotlin.fir.resolve.transformers.* -import org.jetbrains.kotlin.fir.resolve.withExpectedType import org.jetbrains.kotlin.fir.scopes.impl.FirMemberTypeParameterScope import org.jetbrains.kotlin.fir.symbols.constructStarProjectedType import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol @@ -156,7 +155,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor } val delegate = property.delegate if (delegate != null) { - transformPropertyAccessorsWithDelegate(property, delegate) + transformPropertyAccessorsWithDelegate(property) if (property.delegateFieldSymbol != null) { replacePropertyReferenceTypeInDelegateAccessors(property) } @@ -263,8 +262,10 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor (property.delegate as? FirFunctionCall)?.replacePropertyReferenceTypeInDelegateAccessors(property) } - private fun transformPropertyAccessorsWithDelegate(property: FirProperty, delegateExpression: FirExpression) { - context.forPropertyDelegateAccessors(property, delegateExpression, resolutionContext, callCompleter) { + private fun transformPropertyAccessorsWithDelegate(property: FirProperty) { + + context.forPropertyDelegateAccessors(property, resolutionContext, callCompleter) { + // Resolve delegate expression, after that, delegate will contain either expr.provideDelegate or expr if (property.isLocal) { property.transformDelegate(transformer, ResolutionMode.ContextDependentDelegate) } else { @@ -272,9 +273,15 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor property.transformDelegate(transformer, ResolutionMode.ContextDependentDelegate) } } + property.transformAccessors() val completedCalls = completeCandidates() val finalSubstitutor = createFinalSubstitutor() + + // Replace stub types with corresponding type variable types + val stubTypeCompletionResultsWriter = FirStubTypeTransformer(finalSubstitutor) + property.transformSingle(stubTypeCompletionResultsWriter, null) + val callCompletionResultsWriter = callCompleter.createCompletionResultsWriter( finalSubstitutor, mode = FirCallCompletionResultsWriterTransformer.Mode.DelegatedPropertyCompletion @@ -282,6 +289,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor completedCalls.forEach { it.transformSingle(callCompletionResultsWriter, null) } + val declarationCompletionResultsWriter = FirDeclarationCompletionResultsWriter(finalSubstitutor, session.typeApproximator, session.typeContext) property.transformSingle(declarationCompletionResultsWriter, FirDeclarationCompletionResultsWriter.ApproximationData.Default) @@ -294,24 +302,57 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor ): FirStatement { dataFlowAnalyzer.enterDelegateExpression() try { - val delegateProvider = wrappedDelegateExpression.delegateProvider.transformSingle(transformer, data) - when (val calleeReference = (delegateProvider as FirResolvable).calleeReference) { - is FirResolvedNamedReference -> return delegateProvider - is FirErrorReferenceWithCandidate -> { - } - is FirNamedReferenceWithCandidate -> { - val candidate = calleeReference.candidate - if (!candidate.system.hasContradiction) { - return delegateProvider + // First, resolve delegate expression in dependent context + val delegateExpression = + wrappedDelegateExpression.expression.transformSingle(transformer, ResolutionMode.ContextDependent) + + // Second, replace result type of delegate expression with stub type if delegate not yet resolved + if (delegateExpression is FirQualifiedAccess) { + 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) + val delegateExpressionTypeRef = delegateExpression.typeRef + val stubTypeSubstituted = substitutor.substituteOrNull(delegateExpressionTypeRef.coneType) + delegateExpression.replaceTypeRef(delegateExpressionTypeRef.withReplacedConeType(stubTypeSubstituted)) } } - context.inferenceSession.clear() - (delegateProvider as? FirFunctionCall)?.let { dataFlowAnalyzer.dropSubgraphFromCall(it) } + val provideDelegateCall = wrappedDelegateExpression.delegateProvider as FirFunctionCall - return wrappedDelegateExpression.expression - .transformSingle(transformer, data) + // Resolve call for provideDelegate, without completion + provideDelegateCall.transformSingle(this, ResolutionMode.ContextIndependent) + + // 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) + + val stubTypeSubstituted = substitutor.substituteOrSelf(provideDelegateCandidate.substitutor.substituteOrSelf(components.typeFromCallee(provideDelegateCall).type)) + + provideDelegateCall.replaceTypeRef(provideDelegateCall.typeRef.resolvedTypeFromPrototype(stubTypeSubstituted)) + return provideDelegateCall + } + + if (provideDelegateCall.calleeReference is FirResolvedNamedReference) { + return provideDelegateCall + } + + // Otherwise, rollback + (provideDelegateCall as? FirFunctionCall)?.let { dataFlowAnalyzer.dropSubgraphFromCall(it) } + + // Select delegate expression otherwise + return delegateExpression .approximateIfIsIntegerConst() } finally { dataFlowAnalyzer.exitDelegateExpression() @@ -325,7 +366,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor val hadExplicitType = variable.returnTypeRef !is FirImplicitTypeRef if (delegate != null) { - transformPropertyAccessorsWithDelegate(variable, delegate) + transformPropertyAccessorsWithDelegate(variable) if (variable.delegateFieldSymbol != null) { replacePropertyReferenceTypeInDelegateAccessors(variable) }