[FIR] Only add non-subsumed to overridden of intersection override

If an intersection override overrides members A.x, B.x and C.x and
B <: A, then A.x is subsumed by B.x, and we don't add it to the list of
overridden members. This fixes a false-positive MANY_IMPL_MEMBER_NOT_
IMPLEMENTED where an implementation is subsumed by an abstract override.

^KT-57092 Fixed
This commit is contained in:
Kirill Rakhman
2023-03-06 16:00:23 +01:00
committed by Space Team
parent 6afb1b7363
commit 83845fbab5
9 changed files with 64 additions and 36 deletions
@@ -23652,6 +23652,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/tests/override/DelegationVar.kt");
}
@Test
@TestMetadata("diamondWithDiagonal.kt")
public void testDiamondWithDiagonal() throws Exception {
runTest("compiler/testData/diagnostics/tests/override/diamondWithDiagonal.kt");
}
@Test
@TestMetadata("DuplicateMethod.kt")
public void testDuplicateMethod() throws Exception {
@@ -23652,6 +23652,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
runTest("compiler/testData/diagnostics/tests/override/DelegationVar.kt");
}
@Test
@TestMetadata("diamondWithDiagonal.kt")
public void testDiamondWithDiagonal() throws Exception {
runTest("compiler/testData/diagnostics/tests/override/diamondWithDiagonal.kt");
}
@Test
@TestMetadata("DuplicateMethod.kt")
public void testDuplicateMethod() throws Exception {
@@ -23658,6 +23658,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
runTest("compiler/testData/diagnostics/tests/override/DelegationVar.kt");
}
@Test
@TestMetadata("diamondWithDiagonal.kt")
public void testDiamondWithDiagonal() throws Exception {
runTest("compiler/testData/diagnostics/tests/override/diamondWithDiagonal.kt");
}
@Test
@TestMetadata("DuplicateMethod.kt")
public void testDuplicateMethod() throws Exception {
@@ -152,24 +152,25 @@ class FirTypeIntersectionScopeContext(
val groupWithPrivate =
overrideService.extractBothWaysOverridable(allMembersWithScope.maxByVisibility(), allMembersWithScope, overrideChecker)
val group = groupWithPrivate.filter { !Visibilities.isPrivate(it.member.fir.visibility) }.ifEmpty { groupWithPrivate }
val directOverrides = if (forClassUseSiteScope) group.onlyDirectlyInherited() else group
val mostSpecific = overrideService.selectMostSpecificMembers(directOverrides, ReturnTypeCalculatorForFullBodyResolve)
val nonSubsumed = if (forClassUseSiteScope) group.nonSubsumed() else group
val mostSpecific = overrideService.selectMostSpecificMembers(nonSubsumed, ReturnTypeCalculatorForFullBodyResolve)
val nonTrivial = if (forClassUseSiteScope) {
// Create a non-trivial intersection override when the base methods come from different scopes,
// even if one of them is more specific than the others. This is necessary for proper reporting of
// MANY_{IMPL,INTERFACES}_MEMBER_NOT_IMPLEMENTED diagnostics.
// even if one of them is more specific than the others, i.e. when there is more than one method that is not subsumed.
// This is necessary for proper reporting of MANY_{IMPL,INTERFACES}_MEMBER_NOT_IMPLEMENTED diagnostics.
//
// It is also possible to have the opposite case (> 1 most specific member, but all members are from
// the same base scope), but this means there are different instantiations of the same base class,
// which should generally result in INCONSISTENT_TYPE_PARAMETER_VALUES errors.
directOverrides.size > 1 &&
directOverrides.mapTo(mutableSetOf()) { it.member.fir.unwrapSubstitutionOverrides().symbol }.size > 1
nonSubsumed.size > 1 &&
nonSubsumed.mapTo(mutableSetOf()) { it.member.fir.unwrapSubstitutionOverrides().symbol }.size > 1
} else {
// Create a non-trivial intersection override when return types should be intersected.
mostSpecific.size > 1
}
if (nonTrivial) {
result += ResultOfIntersection.NonTrivial(this, mostSpecific, group, containingScope = null)
// Only add non-subsumed members to list of overridden in intersection override.
result += ResultOfIntersection.NonTrivial(this, mostSpecific, overriddenMembers = nonSubsumed, containingScope = null)
} else {
val (member, containingScope) = mostSpecific.first()
result += ResultOfIntersection.SingleMember(member, group, containingScope)
@@ -209,12 +210,16 @@ class FirTypeIntersectionScopeContext(
}.withScope(key.baseScope)
}
private fun <S : FirCallableSymbol<*>> List<MemberWithBaseScope<S>>.onlyDirectlyInherited(): List<MemberWithBaseScope<S>> {
/**
* A callable declaration D [subsumes](https://kotlinlang.org/spec/inheritance.html#matching-and-subsumption-of-declarations)
* a callable declaration B if D overrides B.
*/
private fun <S : FirCallableSymbol<*>> List<MemberWithBaseScope<S>>.nonSubsumed(): List<MemberWithBaseScope<S>> {
val baseMembers = mutableSetOf<FirCallableSymbol<*>>()
for ((member, scope) in this) {
val unwrapped = member.fir.unwrapSubstitutionOverrides().symbol
val unwrapped = member.unwrapSubstitutionOverrides<FirCallableSymbol<*>>()
val addIfDifferent = { it: FirCallableSymbol<*> ->
val symbol = it.fir.unwrapSubstitutionOverrides().symbol
val symbol = it.unwrapSubstitutionOverrides()
if (symbol != unwrapped) {
baseMembers += symbol
}
@@ -226,7 +231,7 @@ class FirTypeIntersectionScopeContext(
scope.processOverriddenProperties(member, addIfDifferent)
}
}
return filter { it.member.fir.unwrapSubstitutionOverrides().symbol !in baseMembers }
return filter { it.member.unwrapSubstitutionOverrides<FirCallableSymbol<*>>() !in baseMembers }
}
private fun <S : FirCallableSymbol<*>> Collection<MemberWithBaseScope<S>>.maxByVisibility(): MemberWithBaseScope<S> {
@@ -301,15 +306,6 @@ class FirTypeIntersectionScopeContext(
return result
}
private inline fun <reified D : FirCallableDeclaration> D.unwrapSubstitutionOverrides(): D {
var current = this
do {
val next = current.originalForSubstitutionOverride ?: return current
current = next
} while (true)
}
private fun <D : FirCallableSymbol<*>> collectRealOverridden(
symbol: D,
scope: FirTypeScope,
@@ -111,6 +111,8 @@ inline fun <reified D : FirCallableDeclaration> D.unwrapSubstitutionOverrides():
inline fun <reified S : FirCallableSymbol<*>> S.unwrapFakeOverrides(): S = fir.unwrapFakeOverrides().symbol as S
inline fun <reified S : FirCallableSymbol<*>> S.unwrapSubstitutionOverrides(): S = fir.unwrapSubstitutionOverrides().symbol as S
private object SubstitutedOverrideOriginalKey : FirDeclarationDataKey()
var <D : FirCallableDeclaration>
@@ -1,16 +0,0 @@
interface A {
fun foo() {}
}
interface B : A {
abstract override fun foo()
}
interface C : A {
abstract override fun foo()
}
interface D : A
// Fake override Z#foo should be abstract
<!MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED!>class Z<!> : B, C, D
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
interface A {
fun foo() {}
}
@@ -0,0 +1,21 @@
// SKIP_TXT
// FIR_IDENTICAL
// ISSUE: KT-57092
interface InterfaceWithDefault {
val hostKind: Int get() = 24
}
interface SubInterfaceWithoutDefault : InterfaceWithDefault {
// SubInterfaceWithoutDefault.hostKind subsumes InterfaceWithDefault.hostKind, therefore no error.
override val hostKind: Int
}
open class ClassWithDefault : InterfaceWithDefault {
override val hostKind: Int get() = 42
}
class InheritsAll :
ClassWithDefault(),
SubInterfaceWithoutDefault,
InterfaceWithDefault
@@ -23658,6 +23658,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/override/DelegationVar.kt");
}
@Test
@TestMetadata("diamondWithDiagonal.kt")
public void testDiamondWithDiagonal() throws Exception {
runTest("compiler/testData/diagnostics/tests/override/diamondWithDiagonal.kt");
}
@Test
@TestMetadata("DuplicateMethod.kt")
public void testDuplicateMethod() throws Exception {