FIR: report ambiguity-like errors in type resolve

This commit is contained in:
Mikhail Glukhikh
2022-03-09 10:25:06 +03:00
committed by Space
parent f073e86c93
commit c8453ec8b4
17 changed files with 132 additions and 34 deletions
@@ -259,8 +259,8 @@ private fun mapSystemHasContradictionError(
qualifiedAccessSource: KtSourceElement?, qualifiedAccessSource: KtSourceElement?,
): List<KtDiagnostic> { ): List<KtDiagnostic> {
val errorsToIgnore = mutableSetOf<ConstraintSystemError>() val errorsToIgnore = mutableSetOf<ConstraintSystemError>()
return buildList<KtDiagnostic> { return buildList {
for (error in diagnostic.candidate.system.errors) { for (error in diagnostic.candidate.errors) {
addIfNotNull( addIfNotNull(
error.toDiagnostic( error.toDiagnostic(
source, source,
@@ -273,7 +273,7 @@ private fun mapSystemHasContradictionError(
} }
}.ifEmpty { }.ifEmpty {
listOfNotNull( listOfNotNull(
diagnostic.candidate.system.errors.firstNotNullOfOrNull { diagnostic.candidate.errors.firstNotNullOfOrNull {
if (it in errorsToIgnore) return@firstNotNullOfOrNull null if (it in errorsToIgnore) return@firstNotNullOfOrNull null
val message = when (it) { val message = when (it) {
is NewConstraintError -> "NewConstraintError at ${it.position}: ${it.lowerType} <!: ${it.upperType}" is NewConstraintError -> "NewConstraintError at ${it.position}: ${it.lowerType} <!: ${it.upperType}"
@@ -284,7 +284,7 @@ private fun mapSystemHasContradictionError(
} }
if (it is NewConstraintError && it.position.from is FixVariableConstraintPosition<*>) { if (it is NewConstraintError && it.position.from is FixVariableConstraintPosition<*>) {
val morePreciseDiagnosticExists = diagnostic.candidate.system.errors.any { other -> val morePreciseDiagnosticExists = diagnostic.candidate.errors.any { other ->
other is NewConstraintError && other.position.from !is FixVariableConstraintPosition<*> other is NewConstraintError && other.position.from !is FixVariableConstraintPosition<*>
} }
if (morePreciseDiagnosticExists) return@firstNotNullOfOrNull null if (morePreciseDiagnosticExists) return@firstNotNullOfOrNull null
@@ -352,7 +352,7 @@ private fun ConstraintSystemError.toDiagnostic(
} }
} }
is NotEnoughInformationForTypeParameter<*> -> { is NotEnoughInformationForTypeParameter<*> -> {
val isDiagnosticRedundant = candidate.system.errors.any { otherError -> val isDiagnosticRedundant = candidate.errors.any { otherError ->
(otherError is ConstrainingTypeIsError && otherError.typeVariable == this.typeVariable) (otherError is ConstrainingTypeIsError && otherError.typeVariable == this.typeVariable)
|| otherError is NewConstraintError || otherError is NewConstraintError
} }
@@ -1245,7 +1245,7 @@ class HtmlFirDump internal constructor(private var linkResolver: FirLinkResolver
is ConeInapplicableCandidateError -> { is ConeInapplicableCandidateError -> {
describeVerbose(diagnostic.candidate.symbol) describeVerbose(diagnostic.candidate.symbol)
br br
diagnostic.candidate.system.errors.forEach { callDiagnostic -> diagnostic.candidate.errors.forEach { callDiagnostic ->
when (callDiagnostic) { when (callDiagnostic) {
is NewConstraintError -> { is NewConstraintError -> {
ident() ident()
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.types.ConeTypeVariable
import org.jetbrains.kotlin.resolve.calls.components.SuspendConversionStrategy import org.jetbrains.kotlin.resolve.calls.components.SuspendConversionStrategy
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
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.resolve.calls.tower.CandidateApplicability
@@ -37,13 +38,16 @@ class Candidate(
) : AbstractCandidate() { ) : AbstractCandidate() {
var systemInitialized: Boolean = false var systemInitialized: Boolean = false
override val system: NewConstraintSystemImpl by lazy(LazyThreadSafetyMode.NONE) { val system: NewConstraintSystemImpl by lazy(LazyThreadSafetyMode.NONE) {
val system = constraintSystemFactory.createConstraintSystem() val system = constraintSystemFactory.createConstraintSystem()
system.addOtherSystem(baseSystem) system.addOtherSystem(baseSystem)
systemInitialized = true systemInitialized = true
system system
} }
override val errors: List<ConstraintSystemError>
get() = system.errors
lateinit var substitutor: ConeSubstitutor lateinit var substitutor: ConeSubstitutor
lateinit var freshVariables: List<ConeTypeVariable> lateinit var freshVariables: List<ConeTypeVariable>
var resultingTypeForCallableReference: ConeKotlinType? = null var resultingTypeForCallableReference: ConeKotlinType? = null
@@ -18,6 +18,10 @@ import org.jetbrains.kotlin.fir.diagnostics.ConeUnexpectedTypeArgumentsError
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.AbstractCallInfo
import org.jetbrains.kotlin.fir.resolve.calls.AbstractCandidate
import org.jetbrains.kotlin.fir.resolve.calls.ReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ResolutionDiagnostic
import org.jetbrains.kotlin.fir.resolve.diagnostics.* import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
@@ -32,6 +36,8 @@ import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.fir.visibilityChecker import org.jetbrains.kotlin.fir.visibilityChecker
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue
@@ -132,7 +138,7 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
candidates.clear() candidates.clear()
} }
if (symbolApplicability == applicability) { if (symbolApplicability == applicability) {
candidates.add(TypeCandidate(symbol, substitutor, diagnostic)) candidates.add(TypeCandidate(symbol, substitutor, diagnostic, symbolApplicability))
} }
} }
@@ -160,7 +166,7 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
TypeResolutionResult.Resolved(candidate) TypeResolutionResult.Resolved(candidate)
} }
candidateCount > 1 -> { candidateCount > 1 -> {
TypeResolutionResult.Ambiguity TypeResolutionResult.Ambiguity(candidates.toList())
} }
candidateCount == 0 -> { candidateCount == 0 -> {
TypeResolutionResult.Unresolved TypeResolutionResult.Unresolved
@@ -170,7 +176,7 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
} }
sealed class TypeResolutionResult { sealed class TypeResolutionResult {
object Ambiguity : TypeResolutionResult() class Ambiguity(val typeCandidates: List<TypeCandidate>) : TypeResolutionResult()
object Unresolved : TypeResolutionResult() object Unresolved : TypeResolutionResult()
class Resolved(val typeCandidate: TypeCandidate) : TypeResolutionResult() class Resolved(val typeCandidate: TypeCandidate) : TypeResolutionResult()
} }
@@ -227,19 +233,25 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
is TypeResolutionResult.Resolved -> { is TypeResolutionResult.Resolved -> {
result.typeCandidate.symbol to result.typeCandidate.substitutor result.typeCandidate.symbol to result.typeCandidate.substitutor
} }
TypeResolutionResult.Ambiguity -> null to null is TypeResolutionResult.Ambiguity -> null to null
TypeResolutionResult.Unresolved -> null to null TypeResolutionResult.Unresolved -> null to null
} }
if (symbol == null || symbol !is FirClassifierSymbol<*>) { if (symbol == null || symbol !is FirClassifierSymbol<*>) {
val diagnostic = if (symbol?.fir is FirEnumEntry) { val diagnostic = when {
if (isOperandOfIsOperator) { symbol?.fir is FirEnumEntry -> {
ConeSimpleDiagnostic("'is' operator can not be applied to an enum entry.", DiagnosticKind.IsEnumEntry) if (isOperandOfIsOperator) {
} else { ConeSimpleDiagnostic("'is' operator can not be applied to an enum entry.", DiagnosticKind.IsEnumEntry)
ConeSimpleDiagnostic("An enum entry should not be used as a type.", DiagnosticKind.EnumEntryAsType) } else {
ConeSimpleDiagnostic("An enum entry should not be used as a type.", DiagnosticKind.EnumEntryAsType)
}
}
result is TypeResolutionResult.Ambiguity -> {
ConeAmbiguityError(typeRef.qualifier.last().name, result.typeCandidates.first().applicability, result.typeCandidates)
}
else -> {
ConeUnresolvedQualifierError(typeRef.render())
} }
} else {
ConeUnresolvedQualifierError(typeRef.render())
} }
return ConeErrorType(diagnostic) return ConeErrorType(diagnostic)
} }
@@ -516,10 +528,30 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
class TypeCandidate( class TypeCandidate(
val symbol: FirBasedSymbol<*>, override val symbol: FirBasedSymbol<*>,
val substitutor: ConeSubstitutor?, val substitutor: ConeSubstitutor?,
val diagnostic: ConeDiagnostic? val diagnostic: ConeDiagnostic?,
) { override val applicability: CandidateApplicability
) : AbstractCandidate() {
override val dispatchReceiverValue: ReceiverValue?
get() = null
override val extensionReceiverValue: ReceiverValue?
get() = null
override val explicitReceiverKind: ExplicitReceiverKind
get() = ExplicitReceiverKind.NO_EXPLICIT_RECEIVER
override val diagnostics: List<ResolutionDiagnostic>
get() = emptyList()
override val errors: List<ConstraintSystemError>
get() = emptyList()
override val callInfo: AbstractCallInfo
get() = throw UnsupportedOperationException("Should not be called")
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other !is TypeCandidate) return false if (other !is TypeCandidate) return false
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.fir.resolve.calls package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError
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.resolve.calls.tower.CandidateApplicability
@@ -17,6 +17,6 @@ abstract class AbstractCandidate {
abstract val explicitReceiverKind: ExplicitReceiverKind abstract val explicitReceiverKind: ExplicitReceiverKind
abstract val callInfo: AbstractCallInfo abstract val callInfo: AbstractCallInfo
abstract val diagnostics: List<ResolutionDiagnostic> abstract val diagnostics: List<ResolutionDiagnostic>
abstract val system: NewConstraintSystemImpl abstract val errors: List<ConstraintSystemError>
abstract val applicability: CandidateApplicability abstract val applicability: CandidateApplicability
} }
@@ -0,0 +1,17 @@
// FILE: a.kt
package a
class X
// FILE: b.kt
package b
class X
// FILE: c.kt
package c
import a.*
import b.*
class Y : <!OVERLOAD_RESOLUTION_AMBIGUITY!>X<!>
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// FILE: a.kt // FILE: a.kt
package a package a
@@ -0,0 +1,17 @@
// FILE: a.kt
package a
class X
// FILE: b.kt
package b
class X
// FILE: c.kt
package c
import a.<!CONFLICTING_IMPORT!>X<!>
import b.<!CONFLICTING_IMPORT!>X<!>
class Y : <!OVERLOAD_RESOLUTION_AMBIGUITY!>X<!>
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// FILE: a.kt // FILE: a.kt
package a package a
@@ -0,0 +1,31 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: a.kt
package a
class A {
class B
}
// FILE: b.kt
package a
class D {
class B
}
// FILE: c.kt
import a.A.<!CONFLICTING_IMPORT!>B<!>
import a.D.<!CONFLICTING_IMPORT!>B<!>
fun test(b: <!OVERLOAD_RESOLUTION_AMBIGUITY!>B<!>) {
<!UNRESOLVED_REFERENCE!>B<!>()
}
// FILE: d.kt
import a.A.*
import a.D.*
// todo ambiguvity here
fun test2(b: <!OVERLOAD_RESOLUTION_AMBIGUITY!>B<!>) {
<!UNRESOLVED_REFERENCE!>B<!>()
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: a.kt // FILE: a.kt
package a package a
@@ -24,4 +24,4 @@ package c
import a.* import a.*
import b.* import b.*
fun test(): <!UNRESOLVED_REFERENCE!>x<!> = d().x() fun test(): <!OVERLOAD_RESOLUTION_AMBIGUITY!>x<!> = d().x()
@@ -28,7 +28,7 @@ import p1.*
import <!DEPRECATION_ERROR!>p2.A<!> import <!DEPRECATION_ERROR!>p2.A<!>
import p3.* import p3.*
<!CONFLICTING_OVERLOADS!>fun test(a: <!UNRESOLVED_REFERENCE!>A<!>)<!> { fun test(a: <!NONE_APPLICABLE!>A<!>) {
a.<!UNRESOLVED_REFERENCE!>v1<!> a.<!UNRESOLVED_REFERENCE!>v1<!>
a.<!UNRESOLVED_REFERENCE!>v2<!> a.<!UNRESOLVED_REFERENCE!>v2<!>
a.<!UNRESOLVED_REFERENCE!>v3<!> a.<!UNRESOLVED_REFERENCE!>v3<!>
@@ -39,7 +39,7 @@ import p1.*
import p2.* import p2.*
import p3.* import p3.*
<!CONFLICTING_OVERLOADS!>fun test(a: <!UNRESOLVED_REFERENCE!>A<!>)<!> { fun test(a: <!NONE_APPLICABLE!>A<!>) {
a.<!UNRESOLVED_REFERENCE!>v1<!> a.<!UNRESOLVED_REFERENCE!>v1<!>
a.<!UNRESOLVED_REFERENCE!>v2<!> a.<!UNRESOLVED_REFERENCE!>v2<!>
a.<!UNRESOLVED_REFERENCE!>v3<!> a.<!UNRESOLVED_REFERENCE!>v3<!>
@@ -23,7 +23,7 @@ class A {
import p1.* import p1.*
import p2.* import p2.*
fun test(a: <!UNRESOLVED_REFERENCE!>A<!>) { fun test(a: <!OVERLOAD_RESOLUTION_AMBIGUITY!>A<!>) {
a.<!UNRESOLVED_REFERENCE!>m1<!>() a.<!UNRESOLVED_REFERENCE!>m1<!>()
a.<!UNRESOLVED_REFERENCE!>m2<!>() a.<!UNRESOLVED_REFERENCE!>m2<!>()
} }
@@ -40,7 +40,7 @@ import p1.*
import p2.* import p2.*
import p3.* import p3.*
fun test(a: <!UNRESOLVED_REFERENCE!>A<!>) { fun test(a: <!OVERLOAD_RESOLUTION_AMBIGUITY!>A<!>) {
a.<!UNRESOLVED_REFERENCE!>v1<!> a.<!UNRESOLVED_REFERENCE!>v1<!>
a.<!UNRESOLVED_REFERENCE!>v2<!> a.<!UNRESOLVED_REFERENCE!>v2<!>
a.<!UNRESOLVED_REFERENCE!>v3<!> a.<!UNRESOLVED_REFERENCE!>v3<!>
@@ -2,5 +2,5 @@ fun <<!REDECLARATION!>T<!>, <!REDECLARATION!>T<!>> Pair() {}
class P<<!REDECLARATION!>T<!>, <!REDECLARATION!>T<!>> {} class P<<!REDECLARATION!>T<!>, <!REDECLARATION!>T<!>> {}
val <<!REDECLARATION, TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER!>T<!>, <!REDECLARATION, TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER!>T<!>> <!UNRESOLVED_REFERENCE!>T<!>.foo : Int val <<!REDECLARATION, TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER!>T<!>, <!REDECLARATION, TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER!>T<!>> <!OVERLOAD_RESOLUTION_AMBIGUITY!>T<!>.foo : Int
get() = 1 get() = 1
@@ -57,7 +57,7 @@ package abc4
import kotlin.<!CONFLICTING_IMPORT!>Throws<!> import kotlin.<!CONFLICTING_IMPORT!>Throws<!>
import kotlin.jvm.<!CONFLICTING_IMPORT!>Throws<!> import kotlin.jvm.<!CONFLICTING_IMPORT!>Throws<!>
@<!UNRESOLVED_REFERENCE!>Throws<!>(Exception::class) @<!OVERLOAD_RESOLUTION_AMBIGUITY!>Throws<!>(Exception::class)
fun foo1() {} fun foo1() {}
@kotlin.Throws(Exception::class) @kotlin.Throws(Exception::class)
@@ -66,7 +66,7 @@ fun foo2() {}
@kotlin.jvm.Throws(Exception::class) @kotlin.jvm.Throws(Exception::class)
fun foo3() {} fun foo3() {}
fun foo5(x: <!UNRESOLVED_REFERENCE!>Throws<!>) {} fun foo5(x: <!OVERLOAD_RESOLUTION_AMBIGUITY!>Throws<!>) {}
fun foo6(x: kotlin.Throws) {} fun foo6(x: kotlin.Throws) {}
fun foo7(x: kotlin.jvm.Throws) {} fun foo7(x: kotlin.jvm.Throws) {}