Skip to content

Commit 442844f

Browse files
dzharkovSpace Team
authored and
Space Team
committed
K2: Fix false-positive overload ambiguity after smart cast
Mostly, the change is just mirroring the same logic from K1 OverloadingConflictResolver ^KT-58524 Fixed
1 parent efdecc6 commit 442844f

File tree

9 files changed

+159
-2
lines changed

9 files changed

+159
-2
lines changed

analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirOverrideUtils.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ fun <D : FirCallableSymbol<*>> filterOutOverridden(
3535
}
3636

3737
// Whether f overrides g
38-
private fun <D : FirCallableSymbol<*>> overrides(
38+
fun <D : FirCallableSymbol<*>> overrides(
3939
f: MemberWithBaseScope<D>,
4040
gMember: D,
4141
processAllOverridden: ProcessAllOverridden<D>,

compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeOverloadConflictResolver.kt

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@ import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
1414
import org.jetbrains.kotlin.fir.resolve.inference.ConeTypeParameterBasedTypeVariable
1515
import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents
1616
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
17+
import org.jetbrains.kotlin.fir.scopes.*
18+
import org.jetbrains.kotlin.fir.scopes.impl.overrides
1719
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
20+
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
21+
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
22+
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
1823
import org.jetbrains.kotlin.fir.types.coneType
24+
import org.jetbrains.kotlin.fir.unwrapSubstitutionOverrides
1925
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
2026
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
2127
import org.jetbrains.kotlin.resolve.calls.results.FlatSignature
@@ -44,6 +50,9 @@ class ConeOverloadConflictResolver(
4450
discriminateAbstracts: Boolean,
4551
): Set<Candidate> = chooseMaximallySpecificCandidates(candidates, discriminateAbstracts, discriminateGenerics = true)
4652

53+
/**
54+
* Partial mirror of [org.jetbrains.kotlin.resolve.calls.results.OverloadingConflictResolver.chooseMaximallySpecificCandidates]
55+
*/
4756
private fun chooseMaximallySpecificCandidates(
4857
candidates: Set<Candidate>,
4958
discriminateAbstracts: Boolean,
@@ -57,8 +66,11 @@ class ConeOverloadConflictResolver(
5766
else
5867
candidates
5968

69+
// The same logic as at
70+
val noOverrides = filterOverrides(fixedCandidates)
71+
6072
return chooseMaximallySpecificCandidates(
61-
fixedCandidates,
73+
noOverrides,
6274
discriminateGenerics,
6375
discriminateAbstracts,
6476
discriminateSAMs = true,
@@ -67,6 +79,55 @@ class ConeOverloadConflictResolver(
6779
)
6880
}
6981

82+
/**
83+
* See K1 version at OverridingUtil.filterOverrides
84+
*/
85+
private fun filterOverrides(
86+
candidateSet: Set<Candidate>,
87+
): Set<Candidate> {
88+
if (candidateSet.size <= 1) return candidateSet
89+
90+
val result = mutableSetOf<Candidate>()
91+
92+
// Assuming `overrides` is a partial order, this loop leaves minimal elements of `candidateSet` in `result`.
93+
// Namely, it leaves in `result` only candidates, for any pair of them (x, y): !x.overrides(y) && !y.overrides(x)
94+
// And for any pair original candidates (x, y) if x.overrides(y) && !y.overrides(x) then `x` belongs `result`
95+
outerLoop@ for (me in candidateSet) {
96+
val iterator = result.iterator()
97+
while (iterator.hasNext()) {
98+
val other = iterator.next()
99+
if (me.overrides(other)) {
100+
iterator.remove()
101+
} else if (other.overrides(me)) {
102+
continue@outerLoop
103+
}
104+
}
105+
106+
result.add(me)
107+
}
108+
109+
require(result.isNotEmpty()) { "All candidates filtered out from $candidateSet" }
110+
return result
111+
}
112+
113+
private fun Candidate.overrides(other: Candidate): Boolean {
114+
if (symbol !is FirCallableSymbol || other.symbol !is FirCallableSymbol) return false
115+
116+
val otherOriginal = other.symbol.unwrapSubstitutionOverrides()
117+
if (symbol.unwrapSubstitutionOverrides<FirCallableSymbol<*>>() == otherOriginal) return true
118+
119+
val scope = originScope as? FirTypeScope ?: return false
120+
121+
@Suppress("UNCHECKED_CAST")
122+
val overriddenProducer = when (symbol) {
123+
is FirNamedFunctionSymbol -> FirTypeScope::processOverriddenFunctions as ProcessAllOverridden<FirCallableSymbol<*>>
124+
is FirPropertySymbol -> FirTypeScope::processOverriddenProperties as ProcessAllOverridden<FirCallableSymbol<*>>
125+
else -> return false
126+
}
127+
128+
return overrides(MemberWithBaseScope(symbol, scope), otherOriginal, overriddenProducer)
129+
}
130+
70131
private fun chooseCandidatesWithMostSpecificInvokeReceiver(candidates: Set<Candidate>): Set<Candidate> {
71132
val propertyReceiverCandidates = candidates.mapTo(mutableSetOf()) {
72133
it.callInfo.candidateForCommonInvokeReceiver
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// ISSUE: KT-58524
2+
3+
interface MyGenericInterface<T> {
4+
fun update(f: (T) -> T) {}
5+
}
6+
7+
interface SubGenericInterface<F> : MyGenericInterface<F> {
8+
override fun update(f: (F) -> F) {}
9+
}
10+
11+
interface SubInterfaceInt : SubGenericInterface<Int> {
12+
override fun update(f: (Int) -> Int) {}
13+
}
14+
15+
fun foo1(a: MyGenericInterface<Number>) {
16+
a <!UNCHECKED_CAST!>as MyGenericInterface<Int><!>
17+
18+
a.update { expectInt(it) }
19+
}
20+
21+
fun foo2(a: MyGenericInterface<Number>) {
22+
a <!UNCHECKED_CAST!>as SubGenericInterface<Int><!>
23+
24+
a.update { expectInt(it) }
25+
}
26+
27+
fun foo3(a: MyGenericInterface<Number>) {
28+
a as SubInterfaceInt
29+
30+
a.update { expectInt(it) }
31+
}
32+
33+
fun expectInt(w: Int): Int = w
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// ISSUE: KT-58524
2+
3+
interface MyGenericInterface<T> {
4+
fun update(f: (T) -> T) {}
5+
}
6+
7+
interface SubGenericInterface<F> : MyGenericInterface<F> {
8+
override fun update(f: (F) -> F) {}
9+
}
10+
11+
interface SubInterfaceInt : SubGenericInterface<Int> {
12+
override fun update(f: (Int) -> Int) {}
13+
}
14+
15+
fun foo1(a: MyGenericInterface<Number>) {
16+
a <!UNCHECKED_CAST!>as MyGenericInterface<Int><!>
17+
18+
<!DEBUG_INFO_SMARTCAST!>a<!>.update { expectInt(it) }
19+
}
20+
21+
fun foo2(a: MyGenericInterface<Number>) {
22+
a <!UNCHECKED_CAST!>as SubGenericInterface<Int><!>
23+
24+
<!DEBUG_INFO_SMARTCAST!>a<!>.update { expectInt(it) }
25+
}
26+
27+
fun foo3(a: MyGenericInterface<Number>) {
28+
a as SubInterfaceInt
29+
30+
<!DEBUG_INFO_SMARTCAST!>a<!>.update { expectInt(it) }
31+
}
32+
33+
fun expectInt(w: Int): Int = w

compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)