diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ManyCandidatesResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ManyCandidatesResolver.kt index dcaa9fe8275..9cb3fb6bda5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ManyCandidatesResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ManyCandidatesResolver.kt @@ -12,9 +12,12 @@ import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage +import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition +import org.jetbrains.kotlin.resolve.calls.inference.model.typeForTypeVariable import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy +import org.jetbrains.kotlin.types.TypeConstructor abstract class ManyCandidatesResolver( private val psiCallResolver: PSICallResolver, @@ -94,11 +97,39 @@ abstract class ManyCandidatesResolver( val allCandidates = arrayListOf>() if (hasOneSuccessfulAndOneErrorCandidate) { - for (callInfo in resolvedCallsInfo) { + val goodCandidate = resolvedCallsInfo.first { it.callResolutionResult.constraintSystem.errors.isEmpty() && it.callResolutionResult.diagnostics.isEmpty() } + val badCandidate = resolvedCallsInfo.first { it.callResolutionResult.constraintSystem.errors.isNotEmpty() || it.callResolutionResult.diagnostics.isNotEmpty()} + + for (callInfo in listOf(goodCandidate, badCandidate)) { + val atomsToAnalyze = mutableListOf(callInfo.callResolutionResult) val system = NewConstraintSystemImpl(callComponents.constraintInjector, builtIns).apply { addOtherSystem(callInfo.callResolutionResult.constraintSystem) + /* + * This is needed for very stupid case, when we have some delegate with good `getValue` and bad `setValue` that + * was provided by some function call with generic (e.g. var x by lazy { "" }) + * The problem is that we want to complete candidates for `getValue` and `setValue` separately, so diagnostics + * from `setValue` don't leak into resolved call of `getValue`, but both calls can have same + * atom for receiver (and type variables from it). After we complete first call, completion of + * second call fails because it's atoms don't contains type variable from receiver, because + * they was completed in first call + * To fix that we add equality constraints from first call to system of second call and create + * stub atoms in order to call completer doesn't fail + */ + if (callInfo === badCandidate) { + for ((typeVariable, fixedType) in allCandidates[0].resolutionResult.constraintSystem.fixedTypeVariables) { + if (typeVariable in this.notFixedTypeVariables) { + val type = (typeVariable as TypeConstructor).typeForTypeVariable() + addEqualityConstraint( + type, + fixedType, + SimpleConstraintSystemConstraintPosition + ) + atomsToAnalyze += StubResolvedAtom(typeVariable) + } + } + } } - runCompletion(system, listOf(callInfo.callResolutionResult)) + runCompletion(system, atomsToAnalyze) val resolutionResult = callInfo.asCallResolutionResult(diagnosticHolder, system) allCandidates += ResolutionResultCallInfo( resolutionResult, diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt index c0753c48b44..0ae7930859b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt @@ -217,6 +217,13 @@ class KotlinConstraintSystemCompleter( typeConstructor.takeIf { c.notFixedTypeVariables.containsKey(typeConstructor) } } + /* + * Hack for completing error candidates in delegate resolve + */ + if (this is StubResolvedAtom && typeVariable in c.notFixedTypeVariables) { + to += typeVariable + } + if (analyzed) { subResolvedAtoms?.forEach { it.process(to) } } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt index 0080bd0eab1..18f2c3f668b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableForLambdaReturnType import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeConstructor import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.model.KotlinTypeMarker @@ -246,4 +247,12 @@ class PartialCallContainer(val result: PartialCallResolutionResult?) { companion object { val empty = PartialCallContainer(null) } +} + +/* + * Used only for delegated properties with one good candidate and one for bad + * e.g. in case `var x by lazy { "" } + */ +class StubResolvedAtom(val typeVariable: TypeConstructor) : ResolvedAtom() { + override val atom: ResolutionAtom? get() = null } \ No newline at end of file