[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:
committed by
Space Team
parent
6afb1b7363
commit
83845fbab5
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+16
-20
@@ -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
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user