Suspend resolution sequence on first inapplicability report

This commit is contained in:
Simon Ogorodnik
2019-05-21 17:55:29 +03:00
committed by Mikhail Glukhikh
parent cb76ea5d14
commit 94dca1d467
3 changed files with 65 additions and 22 deletions
@@ -36,6 +36,9 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.utils.addToStdlib.cast
import java.util.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.createCoroutineUnintercepted
class CallInfo(
val callKind: CallKind,
@@ -55,15 +58,34 @@ class CallInfo(
interface CheckerSink {
fun reportApplicability(new: CandidateApplicability)
suspend fun yield()
suspend fun yieldApplicability(new: CandidateApplicability) {
reportApplicability(new)
yield()
}
val components: InferenceComponents
suspend fun yieldIfNeed()
}
class CheckerSinkImpl(override val components: InferenceComponents) : CheckerSink {
class CheckerSinkImpl(override val components: InferenceComponents, var continuation: Continuation<Unit>? = null) : CheckerSink {
var current = CandidateApplicability.RESOLVED
override fun reportApplicability(new: CandidateApplicability) {
if (new < current) current = new
}
override suspend fun yield() = kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn<Unit> {
continuation = it
kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
}
override suspend fun yieldIfNeed() {
if (current < CandidateApplicability.SYNTHETIC_RESOLVED) {
yield()
}
}
}
@@ -627,6 +649,9 @@ enum class CandidateApplicability {
RESOLVED
}
var ID = ""
class CandidateCollector(val callInfo: CallInfo, val components: InferenceComponents) {
val groupNumbers = mutableListOf<Int>()
@@ -648,12 +673,30 @@ class CandidateCollector(val callInfo: CallInfo, val components: InferenceCompon
): CandidateApplicability {
val sink = CheckerSinkImpl(components)
var finished = false
sink.continuation = suspend {
for (stage in callInfo.callKind.sequence()) {
stage.check(candidate, sink, callInfo)
}
}.createCoroutineUnintercepted(completion = object : Continuation<Unit> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<Unit>) {
result.exceptionOrNull()?.let { throw it }
finished = true
}
})
callInfo.callKind.sequence().forEach {
it.check(candidate, sink, callInfo)
while (!finished) {
sink.continuation!!.resume(Unit)
if (sink.current < CandidateApplicability.SYNTHETIC_RESOLVED) {
break
}
}
return sink.current
}
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystem
internal object CreateFreshTypeVariableSubstitutorStage : ResolutionStage() {
override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
val declaration = candidate.symbol.firUnsafe<FirDeclaration>()
if (declaration !is FirCallableMemberDeclaration || declaration.typeParameters.isEmpty()) {
candidate.substitutor = ConeSubstitutor.Empty
@@ -35,7 +35,7 @@ internal object CreateFreshTypeVariableSubstitutorStage : ResolutionStage() {
// bad function -- error on declaration side
if (csBuilder.hasContradiction) {
sink.reportApplicability(CandidateApplicability.INAPPLICABLE) //TODO: auto report it
sink.yieldApplicability(CandidateApplicability.INAPPLICABLE) //TODO: auto report it
return
}
@@ -28,26 +28,26 @@ import java.lang.IllegalStateException
abstract class ResolutionStage {
abstract fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo)
abstract suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo)
}
abstract class CheckerStage : ResolutionStage()
internal object CheckExplicitReceiverConsistency : ResolutionStage() {
override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
val receiverKind = candidate.explicitReceiverKind
val explicitReceiver = callInfo.explicitReceiver
// TODO: add invoke cases
when (receiverKind) {
NO_EXPLICIT_RECEIVER -> {
if (explicitReceiver != null && explicitReceiver !is FirResolvedQualifier)
return sink.reportApplicability(CandidateApplicability.WRONG_RECEIVER)
return sink.yieldApplicability(CandidateApplicability.WRONG_RECEIVER)
}
EXTENSION_RECEIVER, DISPATCH_RECEIVER -> {
if (explicitReceiver == null) return sink.reportApplicability(CandidateApplicability.WRONG_RECEIVER)
if (explicitReceiver == null) return sink.yieldApplicability(CandidateApplicability.WRONG_RECEIVER)
}
BOTH_RECEIVERS -> {
if (explicitReceiver == null) return sink.reportApplicability(CandidateApplicability.WRONG_RECEIVER)
if (explicitReceiver == null) return sink.yieldApplicability(CandidateApplicability.WRONG_RECEIVER)
// Here we should also check additional invoke receiver
}
}
@@ -97,7 +97,7 @@ internal sealed class CheckReceivers : ResolutionStage() {
abstract fun ExplicitReceiverKind.shouldBeResolvedAsImplicit(): Boolean
override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
val receiverParameterValue = candidate.getReceiverValue()
val explicitReceiverExpression = callInfo.explicitReceiver
val explicitReceiverKind = candidate.explicitReceiverKind
@@ -121,21 +121,21 @@ internal sealed class CheckReceivers : ResolutionStage() {
}
internal object MapArguments : ResolutionStage() {
override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
val symbol = candidate.symbol as? FirFunctionSymbol ?: return sink.reportApplicability(CandidateApplicability.HIDDEN)
val function = symbol.firUnsafe<FirFunction>()
val processor = FirCallArgumentsProcessor(function, callInfo.arguments)
val mappingResult = processor.process()
candidate.argumentMapping = mappingResult.argumentMapping
if (!mappingResult.isSuccess) {
return sink.reportApplicability(CandidateApplicability.PARAMETER_MAPPING_ERROR)
return sink.yieldApplicability(CandidateApplicability.PARAMETER_MAPPING_ERROR)
}
}
}
internal object CheckArguments : CheckerStage() {
override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
val argumentMapping =
candidate.argumentMapping ?: throw IllegalStateException("Argument should be already mapped while checking arguments!")
for ((argument, parameter) in argumentMapping) {
@@ -147,16 +147,16 @@ internal object CheckArguments : CheckerStage() {
typeProvider = callInfo.typeProvider,
sink = sink
)
}
if (candidate.system.hasContradiction) {
sink.reportApplicability(CandidateApplicability.INAPPLICABLE)
if (candidate.system.hasContradiction) {
sink.yieldApplicability(CandidateApplicability.INAPPLICABLE)
}
sink.yield()
}
}
}
internal object DiscriminateSynthetics : CheckerStage() {
override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
if (candidate.symbol is SyntheticSymbol) {
sink.reportApplicability(CandidateApplicability.SYNTHETIC_RESOLVED)
}
@@ -174,7 +174,7 @@ internal object CheckVisibility : CheckerStage() {
}
}
override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
val symbol = candidate.symbol
val declaration = symbol.firSafeNullable<FirMemberDeclaration>()
if (declaration != null && !declaration.visibility.isPublicAPI) {
@@ -200,7 +200,7 @@ internal object CheckVisibility : CheckerStage() {
}
if (!visible) {
sink.reportApplicability(CandidateApplicability.HIDDEN)
sink.yieldApplicability(CandidateApplicability.HIDDEN)
}
}
}