[FIR] Report ResolutionDiagnostics instead of applicability in resolve

This commit is contained in:
Dmitriy Novozhilov
2020-09-08 12:33:40 +03:00
parent a148db9d81
commit edb1273355
7 changed files with 44 additions and 41 deletions
@@ -28,7 +28,6 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.types.model.CaptureStatus import org.jetbrains.kotlin.types.model.CaptureStatus
fun Candidate.resolveArgumentExpression( fun Candidate.resolveArgumentExpression(
@@ -202,7 +201,7 @@ fun Candidate.resolvePlainExpressionArgument(
private fun Candidate.checkApplicabilityForIntegerOperatorCall(sink: CheckerSink, argument: FirExpression) { private fun Candidate.checkApplicabilityForIntegerOperatorCall(sink: CheckerSink, argument: FirExpression) {
if (symbol.fir !is FirIntegerOperator) return if (symbol.fir !is FirIntegerOperator) return
if (argument !is FirConstExpression<*> && argument !is FirIntegerOperatorCall) { if (argument !is FirConstExpression<*> && argument !is FirIntegerOperatorCall) {
sink.reportApplicability(CandidateApplicability.INAPPLICABLE) sink.reportDiagnostic(InapplicableCandidate)
} }
} }
@@ -265,7 +264,7 @@ private fun checkApplicabilityForArgumentType(
if (expectedType == null) return if (expectedType == null) return
if (isReceiver && isDispatch) { if (isReceiver && isDispatch) {
if (!expectedType.isNullable && argumentType.isMarkedNullable) { if (!expectedType.isNullable && argumentType.isMarkedNullable) {
sink.reportApplicability(CandidateApplicability.INAPPLICABLE_WRONG_RECEIVER) sink.reportDiagnostic(InapplicableWrongReceiver)
} }
return return
} }
@@ -278,10 +277,10 @@ private fun checkApplicabilityForArgumentType(
val nullableExpectedType = expectedType.withNullability(ConeNullability.NULLABLE, sink.components.session.typeContext) val nullableExpectedType = expectedType.withNullability(ConeNullability.NULLABLE, sink.components.session.typeContext)
if (csBuilder.addSubtypeConstraintIfCompatible(argumentType, nullableExpectedType, position)) { if (csBuilder.addSubtypeConstraintIfCompatible(argumentType, nullableExpectedType, position)) {
sink.reportApplicability(CandidateApplicability.INAPPLICABLE_WRONG_RECEIVER) // TODO sink.reportDiagnostic(InapplicableWrongReceiver) // TODO
} else { } else {
csBuilder.addSubtypeConstraint(argumentType, expectedType, position) csBuilder.addSubtypeConstraint(argumentType, expectedType, position)
sink.reportApplicability(CandidateApplicability.INAPPLICABLE_WRONG_RECEIVER) sink.reportDiagnostic(InapplicableWrongReceiver)
} }
} }
} }
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
abstract class CheckerSink { abstract class CheckerSink {
abstract fun reportApplicability(new: CandidateApplicability) abstract fun reportDiagnostic(diagnostic: ResolutionDiagnostic)
abstract val components: InferenceComponents abstract val components: InferenceComponents
@@ -29,8 +29,8 @@ suspend inline fun CheckerSink.yieldIfNeed() {
} }
} }
suspend inline fun CheckerSink.yieldApplicability(new: CandidateApplicability) { suspend inline fun CheckerSink.yieldDiagnostic(diagnostic: ResolutionDiagnostic) {
reportApplicability(new) reportDiagnostic(diagnostic)
yieldIfNeed() yieldIfNeed()
} }
@@ -39,11 +39,17 @@ class CheckerSinkImpl(
var continuation: Continuation<Unit>? = null, var continuation: Continuation<Unit>? = null,
val stopOnFirstError: Boolean = true val stopOnFirstError: Boolean = true
) : CheckerSink() { ) : CheckerSink() {
var current = CandidateApplicability.RESOLVED var currentApplicability = CandidateApplicability.RESOLVED
private set private set
override fun reportApplicability(new: CandidateApplicability) { private val _diagnostics: MutableList<ResolutionDiagnostic> = mutableListOf()
if (new < current) current = new
val diagnostics: List<ResolutionDiagnostic>
get() = _diagnostics
override fun reportDiagnostic(diagnostic: ResolutionDiagnostic) {
_diagnostics += diagnostic
if (diagnostic.applicability < currentApplicability) currentApplicability = diagnostic.applicability
} }
@PrivateForInline @PrivateForInline
@@ -53,6 +59,6 @@ class CheckerSinkImpl(
} }
override val needYielding: Boolean override val needYielding: Boolean
get() = stopOnFirstError && !current.isSuccess get() = stopOnFirstError && !currentApplicability.isSuccess
} }
@@ -19,7 +19,6 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.FirTypePlaceholderProjection import org.jetbrains.kotlin.fir.types.impl.FirTypePlaceholderProjection
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
internal object CreateFreshTypeVariableSubstitutorStage : ResolutionStage() { internal object CreateFreshTypeVariableSubstitutorStage : ResolutionStage() {
override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) { override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
@@ -37,7 +36,7 @@ internal object CreateFreshTypeVariableSubstitutorStage : ResolutionStage() {
// bad function -- error on declaration side // bad function -- error on declaration side
if (csBuilder.hasContradiction) { if (csBuilder.hasContradiction) {
sink.yieldApplicability(CandidateApplicability.INAPPLICABLE) //TODO: auto report it sink.yieldDiagnostic(InapplicableCandidate) //TODO: auto report it
return return
} }
@@ -50,3 +50,11 @@ class NameNotFound(
override val argument: FirExpression, override val argument: FirExpression,
val function: FirFunction<*> val function: FirFunction<*>
) : InapplicableArgumentDiagnostic() ) : InapplicableArgumentDiagnostic()
object InapplicableCandidate : ResolutionDiagnostic(CandidateApplicability.INAPPLICABLE)
object HiddenCandidate : ResolutionDiagnostic(CandidateApplicability.HIDDEN)
object ResolvedWithLowPriority : ResolutionDiagnostic(CandidateApplicability.RESOLVED_LOW_PRIORITY)
object InapplicableWrongReceiver : ResolutionDiagnostic(CandidateApplicability.INAPPLICABLE_WRONG_RECEIVER)
@@ -36,10 +36,11 @@ class ResolutionStageRunner(val components: InferenceComponents) {
while (!finished) { while (!finished) {
sink.continuation!!.resume(Unit) sink.continuation!!.resume(Unit)
if (!sink.current.isSuccess) { if (!sink.currentApplicability.isSuccess) {
break break
} }
} }
return sink.current candidate.diagnostics += sink.diagnostics
return sink.currentApplicability
} }
} }
@@ -28,9 +28,6 @@ import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.* import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.utils.addToStdlib.min
abstract class ResolutionStage { abstract class ResolutionStage {
abstract suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) abstract suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo)
@@ -46,17 +43,17 @@ internal object CheckExplicitReceiverConsistency : ResolutionStage() {
when (receiverKind) { when (receiverKind) {
NO_EXPLICIT_RECEIVER -> { NO_EXPLICIT_RECEIVER -> {
if (explicitReceiver != null && explicitReceiver !is FirResolvedQualifier && !explicitReceiver.isSuperReferenceExpression()) { if (explicitReceiver != null && explicitReceiver !is FirResolvedQualifier && !explicitReceiver.isSuperReferenceExpression()) {
return sink.yieldApplicability(CandidateApplicability.INAPPLICABLE_WRONG_RECEIVER) return sink.yieldDiagnostic(InapplicableWrongReceiver)
} }
} }
EXTENSION_RECEIVER, DISPATCH_RECEIVER -> { EXTENSION_RECEIVER, DISPATCH_RECEIVER -> {
if (explicitReceiver == null) { if (explicitReceiver == null) {
return sink.yieldApplicability(CandidateApplicability.INAPPLICABLE_WRONG_RECEIVER) return sink.yieldDiagnostic(InapplicableWrongReceiver)
} }
} }
BOTH_RECEIVERS -> { BOTH_RECEIVERS -> {
if (explicitReceiver == null) { if (explicitReceiver == null) {
return sink.yieldApplicability(CandidateApplicability.INAPPLICABLE_WRONG_RECEIVER) return sink.yieldDiagnostic(InapplicableWrongReceiver)
} }
// Here we should also check additional invoke receiver // Here we should also check additional invoke receiver
} }
@@ -152,20 +149,14 @@ private fun FirExpression.isSuperReferenceExpression(): Boolean {
internal object MapArguments : ResolutionStage() { internal object MapArguments : ResolutionStage() {
override suspend 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 symbol = candidate.symbol as? FirFunctionSymbol<*> ?: return sink.reportDiagnostic(HiddenCandidate)
val function = symbol.fir val function = symbol.fir
val mapping = mapArguments(callInfo.arguments, function) val mapping = mapArguments(callInfo.arguments, function)
candidate.argumentMapping = mapping.toArgumentToParameterMapping() candidate.argumentMapping = mapping.toArgumentToParameterMapping()
var applicability = CandidateApplicability.RESOLVED mapping.diagnostics.forEach(sink::reportDiagnostic)
mapping.diagnostics.forEach { sink.yieldIfNeed()
candidate.diagnostics += it
applicability = min(applicability, it.applicability)
}
if (applicability < CandidateApplicability.RESOLVED) {
return sink.yieldApplicability(applicability)
}
} }
} }
@@ -182,7 +173,7 @@ internal object CheckArguments : CheckerStage() {
sink = sink sink = sink
) )
if (candidate.system.hasContradiction) { if (candidate.system.hasContradiction) {
sink.yieldApplicability(CandidateApplicability.INAPPLICABLE) sink.yieldDiagnostic(InapplicableCandidate)
} }
sink.yieldIfNeed() sink.yieldIfNeed()
} }
@@ -195,7 +186,7 @@ internal object EagerResolveOfCallableReferences : CheckerStage() {
for (atom in candidate.postponedAtoms) { for (atom in candidate.postponedAtoms) {
if (atom is ResolvedCallableReferenceAtom) { if (atom is ResolvedCallableReferenceAtom) {
if (!candidate.bodyResolveComponents.callResolver.resolveCallableReference(candidate.csBuilder, atom)) { if (!candidate.bodyResolveComponents.callResolver.resolveCallableReference(candidate.csBuilder, atom)) {
sink.yieldApplicability(CandidateApplicability.INAPPLICABLE) sink.yieldDiagnostic(InapplicableCandidate)
} }
} }
} }
@@ -264,7 +255,7 @@ internal object CheckCallableReferenceExpectedType : CheckerStage() {
} }
if (!isApplicable) { if (!isApplicable) {
sink.yieldApplicability(CandidateApplicability.INAPPLICABLE) sink.yieldDiagnostic(InapplicableCandidate)
} }
} }
} }
@@ -366,7 +357,7 @@ private fun FirSession.createAdaptedKFunctionType(
internal object DiscriminateSynthetics : CheckerStage() { internal object DiscriminateSynthetics : CheckerStage() {
override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) { override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
if (candidate.symbol is SyntheticSymbol) { if (candidate.symbol is SyntheticSymbol) {
sink.reportApplicability(CandidateApplicability.RESOLVED_LOW_PRIORITY) sink.reportDiagnostic(ResolvedWithLowPriority)
} }
} }
} }
@@ -389,7 +380,7 @@ internal object CheckVisibility : CheckerStage() {
if (classSymbol is FirRegularClassSymbol) { if (classSymbol is FirRegularClassSymbol) {
if (classSymbol.fir.classKind.isSingleton) { if (classSymbol.fir.classKind.isSingleton) {
sink.yieldApplicability(CandidateApplicability.HIDDEN) sink.yieldDiagnostic(HiddenCandidate)
} }
checkVisibility(classSymbol.fir, classSymbol, sink, candidate, visibilityChecker) checkVisibility(classSymbol.fir, classSymbol, sink, candidate, visibilityChecker)
} }
@@ -404,7 +395,7 @@ internal object CheckVisibility : CheckerStage() {
visibilityChecker: FirVisibilityChecker visibilityChecker: FirVisibilityChecker
): Boolean { ): Boolean {
if (!visibilityChecker.isVisible(declaration, symbol, candidate)) { if (!visibilityChecker.isVisible(declaration, symbol, candidate)) {
sink.yieldApplicability(CandidateApplicability.HIDDEN) sink.yieldDiagnostic(HiddenCandidate)
return false return false
} }
return true return true
@@ -428,7 +419,7 @@ internal object CheckLowPriorityInOverloadResolution : CheckerStage() {
} }
if (hasLowPriorityAnnotation) { if (hasLowPriorityAnnotation) {
sink.reportApplicability(CandidateApplicability.RESOLVED_LOW_PRIORITY) sink.reportDiagnostic(ResolvedWithLowPriority)
} }
} }
} }
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRefsOwner import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRefsOwner
import org.jetbrains.kotlin.fir.types.FirTypeProjection import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.fir.types.impl.FirTypePlaceholderProjection import org.jetbrains.kotlin.fir.types.impl.FirTypePlaceholderProjection
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
sealed class TypeArgumentMapping { sealed class TypeArgumentMapping {
abstract operator fun get(typeParameterIndex: Int): FirTypeProjection abstract operator fun get(typeParameterIndex: Int): FirTypeProjection
@@ -38,7 +37,7 @@ internal object MapTypeArguments : ResolutionStage() {
candidate.typeArgumentMapping = TypeArgumentMapping.Mapped(typeArguments) candidate.typeArgumentMapping = TypeArgumentMapping.Mapped(typeArguments)
} else { } else {
candidate.typeArgumentMapping = TypeArgumentMapping.Mapped(emptyList()) candidate.typeArgumentMapping = TypeArgumentMapping.Mapped(emptyList())
sink.yieldApplicability(CandidateApplicability.INAPPLICABLE) sink.yieldDiagnostic(InapplicableCandidate)
} }
} }
} }
@@ -46,7 +45,7 @@ internal object MapTypeArguments : ResolutionStage() {
internal object NoTypeArguments : ResolutionStage() { internal object NoTypeArguments : ResolutionStage() {
override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) { override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
if (callInfo.typeArguments.isNotEmpty()) { if (callInfo.typeArguments.isNotEmpty()) {
sink.yieldApplicability(CandidateApplicability.INAPPLICABLE) sink.yieldDiagnostic(InapplicableCandidate)
} }
candidate.typeArgumentMapping = TypeArgumentMapping.NoExplicitArguments candidate.typeArgumentMapping = TypeArgumentMapping.NoExplicitArguments
} }