[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");
|
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
|
@Test
|
||||||
@TestMetadata("DuplicateMethod.kt")
|
@TestMetadata("DuplicateMethod.kt")
|
||||||
public void testDuplicateMethod() throws Exception {
|
public void testDuplicateMethod() throws Exception {
|
||||||
|
|||||||
+6
@@ -23652,6 +23652,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
|||||||
runTest("compiler/testData/diagnostics/tests/override/DelegationVar.kt");
|
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
|
@Test
|
||||||
@TestMetadata("DuplicateMethod.kt")
|
@TestMetadata("DuplicateMethod.kt")
|
||||||
public void testDuplicateMethod() throws Exception {
|
public void testDuplicateMethod() throws Exception {
|
||||||
|
|||||||
+6
@@ -23658,6 +23658,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
|||||||
runTest("compiler/testData/diagnostics/tests/override/DelegationVar.kt");
|
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
|
@Test
|
||||||
@TestMetadata("DuplicateMethod.kt")
|
@TestMetadata("DuplicateMethod.kt")
|
||||||
public void testDuplicateMethod() throws Exception {
|
public void testDuplicateMethod() throws Exception {
|
||||||
|
|||||||
+16
-20
@@ -152,24 +152,25 @@ class FirTypeIntersectionScopeContext(
|
|||||||
val groupWithPrivate =
|
val groupWithPrivate =
|
||||||
overrideService.extractBothWaysOverridable(allMembersWithScope.maxByVisibility(), allMembersWithScope, overrideChecker)
|
overrideService.extractBothWaysOverridable(allMembersWithScope.maxByVisibility(), allMembersWithScope, overrideChecker)
|
||||||
val group = groupWithPrivate.filter { !Visibilities.isPrivate(it.member.fir.visibility) }.ifEmpty { groupWithPrivate }
|
val group = groupWithPrivate.filter { !Visibilities.isPrivate(it.member.fir.visibility) }.ifEmpty { groupWithPrivate }
|
||||||
val directOverrides = if (forClassUseSiteScope) group.onlyDirectlyInherited() else group
|
val nonSubsumed = if (forClassUseSiteScope) group.nonSubsumed() else group
|
||||||
val mostSpecific = overrideService.selectMostSpecificMembers(directOverrides, ReturnTypeCalculatorForFullBodyResolve)
|
val mostSpecific = overrideService.selectMostSpecificMembers(nonSubsumed, ReturnTypeCalculatorForFullBodyResolve)
|
||||||
val nonTrivial = if (forClassUseSiteScope) {
|
val nonTrivial = if (forClassUseSiteScope) {
|
||||||
// Create a non-trivial intersection override when the base methods come from different scopes,
|
// 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
|
// even if one of them is more specific than the others, i.e. when there is more than one method that is not subsumed.
|
||||||
// MANY_{IMPL,INTERFACES}_MEMBER_NOT_IMPLEMENTED diagnostics.
|
// 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
|
// 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,
|
// 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.
|
// which should generally result in INCONSISTENT_TYPE_PARAMETER_VALUES errors.
|
||||||
directOverrides.size > 1 &&
|
nonSubsumed.size > 1 &&
|
||||||
directOverrides.mapTo(mutableSetOf()) { it.member.fir.unwrapSubstitutionOverrides().symbol }.size > 1
|
nonSubsumed.mapTo(mutableSetOf()) { it.member.fir.unwrapSubstitutionOverrides().symbol }.size > 1
|
||||||
} else {
|
} else {
|
||||||
// Create a non-trivial intersection override when return types should be intersected.
|
// Create a non-trivial intersection override when return types should be intersected.
|
||||||
mostSpecific.size > 1
|
mostSpecific.size > 1
|
||||||
}
|
}
|
||||||
if (nonTrivial) {
|
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 {
|
} else {
|
||||||
val (member, containingScope) = mostSpecific.first()
|
val (member, containingScope) = mostSpecific.first()
|
||||||
result += ResultOfIntersection.SingleMember(member, group, containingScope)
|
result += ResultOfIntersection.SingleMember(member, group, containingScope)
|
||||||
@@ -209,12 +210,16 @@ class FirTypeIntersectionScopeContext(
|
|||||||
}.withScope(key.baseScope)
|
}.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<*>>()
|
val baseMembers = mutableSetOf<FirCallableSymbol<*>>()
|
||||||
for ((member, scope) in this) {
|
for ((member, scope) in this) {
|
||||||
val unwrapped = member.fir.unwrapSubstitutionOverrides().symbol
|
val unwrapped = member.unwrapSubstitutionOverrides<FirCallableSymbol<*>>()
|
||||||
val addIfDifferent = { it: FirCallableSymbol<*> ->
|
val addIfDifferent = { it: FirCallableSymbol<*> ->
|
||||||
val symbol = it.fir.unwrapSubstitutionOverrides().symbol
|
val symbol = it.unwrapSubstitutionOverrides()
|
||||||
if (symbol != unwrapped) {
|
if (symbol != unwrapped) {
|
||||||
baseMembers += symbol
|
baseMembers += symbol
|
||||||
}
|
}
|
||||||
@@ -226,7 +231,7 @@ class FirTypeIntersectionScopeContext(
|
|||||||
scope.processOverriddenProperties(member, addIfDifferent)
|
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> {
|
private fun <S : FirCallableSymbol<*>> Collection<MemberWithBaseScope<S>>.maxByVisibility(): MemberWithBaseScope<S> {
|
||||||
@@ -301,15 +306,6 @@ class FirTypeIntersectionScopeContext(
|
|||||||
return result
|
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(
|
private fun <D : FirCallableSymbol<*>> collectRealOverridden(
|
||||||
symbol: D,
|
symbol: D,
|
||||||
scope: FirTypeScope,
|
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.unwrapFakeOverrides(): S = fir.unwrapFakeOverrides().symbol as S
|
||||||
|
|
||||||
|
inline fun <reified S : FirCallableSymbol<*>> S.unwrapSubstitutionOverrides(): S = fir.unwrapSubstitutionOverrides().symbol as S
|
||||||
|
|
||||||
private object SubstitutedOverrideOriginalKey : FirDeclarationDataKey()
|
private object SubstitutedOverrideOriginalKey : FirDeclarationDataKey()
|
||||||
|
|
||||||
var <D : FirCallableDeclaration>
|
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 {
|
interface A {
|
||||||
fun foo() {}
|
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");
|
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
|
@Test
|
||||||
@TestMetadata("DuplicateMethod.kt")
|
@TestMetadata("DuplicateMethod.kt")
|
||||||
public void testDuplicateMethod() throws Exception {
|
public void testDuplicateMethod() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user