K2: Fix false-positive overload ambiguity after smart cast

Mostly, the change is just mirroring the same logic from K1
OverloadingConflictResolver

^KT-58524 Fixed
This commit is contained in:
Denis.Zharkov
2023-05-09 16:52:47 +02:00
committed by Space Team
parent efdecc63cb
commit 442844f165
9 changed files with 159 additions and 2 deletions
@@ -27317,6 +27317,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/extensionReceiverAndVarargs.kt");
}
@Test
@TestMetadata("filteringOutOverrides.kt")
public void testFilteringOutOverrides() throws Exception {
runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.kt");
}
@Test
@TestMetadata("genericClash.kt")
public void testGenericClash() throws Exception {
@@ -27317,6 +27317,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/extensionReceiverAndVarargs.kt");
}
@Test
@TestMetadata("filteringOutOverrides.kt")
public void testFilteringOutOverrides() throws Exception {
runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.kt");
}
@Test
@TestMetadata("genericClash.kt")
public void testGenericClash() throws Exception {
@@ -27317,6 +27317,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/extensionReceiverAndVarargs.kt");
}
@Test
@TestMetadata("filteringOutOverrides.kt")
public void testFilteringOutOverrides() throws Exception {
runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.kt");
}
@Test
@TestMetadata("genericClash.kt")
public void testGenericClash() throws Exception {
@@ -27329,6 +27329,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/extensionReceiverAndVarargs.kt");
}
@Test
@TestMetadata("filteringOutOverrides.kt")
public void testFilteringOutOverrides() throws Exception {
runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.kt");
}
@Test
@TestMetadata("genericClash.kt")
public void testGenericClash() throws Exception {
@@ -35,7 +35,7 @@ fun <D : FirCallableSymbol<*>> filterOutOverridden(
}
// Whether f overrides g
private fun <D : FirCallableSymbol<*>> overrides(
fun <D : FirCallableSymbol<*>> overrides(
f: MemberWithBaseScope<D>,
gMember: D,
processAllOverridden: ProcessAllOverridden<D>,
@@ -14,8 +14,14 @@ import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.inference.ConeTypeParameterBasedTypeVariable
import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.overrides
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.unwrapSubstitutionOverrides
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
import org.jetbrains.kotlin.resolve.calls.results.FlatSignature
@@ -44,6 +50,9 @@ class ConeOverloadConflictResolver(
discriminateAbstracts: Boolean,
): Set<Candidate> = chooseMaximallySpecificCandidates(candidates, discriminateAbstracts, discriminateGenerics = true)
/**
* Partial mirror of [org.jetbrains.kotlin.resolve.calls.results.OverloadingConflictResolver.chooseMaximallySpecificCandidates]
*/
private fun chooseMaximallySpecificCandidates(
candidates: Set<Candidate>,
discriminateAbstracts: Boolean,
@@ -57,8 +66,11 @@ class ConeOverloadConflictResolver(
else
candidates
// The same logic as at
val noOverrides = filterOverrides(fixedCandidates)
return chooseMaximallySpecificCandidates(
fixedCandidates,
noOverrides,
discriminateGenerics,
discriminateAbstracts,
discriminateSAMs = true,
@@ -67,6 +79,55 @@ class ConeOverloadConflictResolver(
)
}
/**
* See K1 version at OverridingUtil.filterOverrides
*/
private fun filterOverrides(
candidateSet: Set<Candidate>,
): Set<Candidate> {
if (candidateSet.size <= 1) return candidateSet
val result = mutableSetOf<Candidate>()
// Assuming `overrides` is a partial order, this loop leaves minimal elements of `candidateSet` in `result`.
// Namely, it leaves in `result` only candidates, for any pair of them (x, y): !x.overrides(y) && !y.overrides(x)
// And for any pair original candidates (x, y) if x.overrides(y) && !y.overrides(x) then `x` belongs `result`
outerLoop@ for (me in candidateSet) {
val iterator = result.iterator()
while (iterator.hasNext()) {
val other = iterator.next()
if (me.overrides(other)) {
iterator.remove()
} else if (other.overrides(me)) {
continue@outerLoop
}
}
result.add(me)
}
require(result.isNotEmpty()) { "All candidates filtered out from $candidateSet" }
return result
}
private fun Candidate.overrides(other: Candidate): Boolean {
if (symbol !is FirCallableSymbol || other.symbol !is FirCallableSymbol) return false
val otherOriginal = other.symbol.unwrapSubstitutionOverrides()
if (symbol.unwrapSubstitutionOverrides<FirCallableSymbol<*>>() == otherOriginal) return true
val scope = originScope as? FirTypeScope ?: return false
@Suppress("UNCHECKED_CAST")
val overriddenProducer = when (symbol) {
is FirNamedFunctionSymbol -> FirTypeScope::processOverriddenFunctions as ProcessAllOverridden<FirCallableSymbol<*>>
is FirPropertySymbol -> FirTypeScope::processOverriddenProperties as ProcessAllOverridden<FirCallableSymbol<*>>
else -> return false
}
return overrides(MemberWithBaseScope(symbol, scope), otherOriginal, overriddenProducer)
}
private fun chooseCandidatesWithMostSpecificInvokeReceiver(candidates: Set<Candidate>): Set<Candidate> {
val propertyReceiverCandidates = candidates.mapTo(mutableSetOf()) {
it.callInfo.candidateForCommonInvokeReceiver
@@ -0,0 +1,33 @@
// ISSUE: KT-58524
interface MyGenericInterface<T> {
fun update(f: (T) -> T) {}
}
interface SubGenericInterface<F> : MyGenericInterface<F> {
override fun update(f: (F) -> F) {}
}
interface SubInterfaceInt : SubGenericInterface<Int> {
override fun update(f: (Int) -> Int) {}
}
fun foo1(a: MyGenericInterface<Number>) {
a <!UNCHECKED_CAST!>as MyGenericInterface<Int><!>
a.update { expectInt(it) }
}
fun foo2(a: MyGenericInterface<Number>) {
a <!UNCHECKED_CAST!>as SubGenericInterface<Int><!>
a.update { expectInt(it) }
}
fun foo3(a: MyGenericInterface<Number>) {
a as SubInterfaceInt
a.update { expectInt(it) }
}
fun expectInt(w: Int): Int = w
@@ -0,0 +1,33 @@
// ISSUE: KT-58524
interface MyGenericInterface<T> {
fun update(f: (T) -> T) {}
}
interface SubGenericInterface<F> : MyGenericInterface<F> {
override fun update(f: (F) -> F) {}
}
interface SubInterfaceInt : SubGenericInterface<Int> {
override fun update(f: (Int) -> Int) {}
}
fun foo1(a: MyGenericInterface<Number>) {
a <!UNCHECKED_CAST!>as MyGenericInterface<Int><!>
<!DEBUG_INFO_SMARTCAST!>a<!>.update { expectInt(it) }
}
fun foo2(a: MyGenericInterface<Number>) {
a <!UNCHECKED_CAST!>as SubGenericInterface<Int><!>
<!DEBUG_INFO_SMARTCAST!>a<!>.update { expectInt(it) }
}
fun foo3(a: MyGenericInterface<Number>) {
a as SubInterfaceInt
<!DEBUG_INFO_SMARTCAST!>a<!>.update { expectInt(it) }
}
fun expectInt(w: Int): Int = w
@@ -28101,6 +28101,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/extensionReceiverAndVarargs.kt");
}
@Test
@TestMetadata("filteringOutOverrides.kt")
public void testFilteringOutOverrides() throws Exception {
runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.kt");
}
@Test
@TestMetadata("genericClash.kt")
public void testGenericClash() throws Exception {