Improve fake override construction for expected classes
Fake overrides for abstract members from expected classes should become non-abstract (final, in fact) in non-abstract expected subclasses #KT-22031 Fixed
This commit is contained in:
+55
@@ -0,0 +1,55 @@
|
|||||||
|
// !LANGUAGE: +MultiPlatformProjects
|
||||||
|
// MODULE: m1-common
|
||||||
|
// FILE: common.kt
|
||||||
|
|
||||||
|
expect abstract class BaseA() {
|
||||||
|
abstract fun foo()
|
||||||
|
}
|
||||||
|
expect open class BaseAImpl() : BaseA
|
||||||
|
|
||||||
|
class DerivedA1 : BaseAImpl()
|
||||||
|
class DerivedA2 : BaseAImpl() {
|
||||||
|
<!OVERRIDING_FINAL_MEMBER!>override<!> fun foo() = super.foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
expect interface BaseB {
|
||||||
|
fun foo()
|
||||||
|
}
|
||||||
|
expect open class BaseBImpl() : BaseB
|
||||||
|
|
||||||
|
class DerivedB1 : BaseBImpl()
|
||||||
|
class DerivedB2 : BaseBImpl() {
|
||||||
|
<!OVERRIDING_FINAL_MEMBER!>override<!> fun foo() = super.foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
expect interface BaseC {
|
||||||
|
fun foo()
|
||||||
|
}
|
||||||
|
expect abstract class BaseCImpl() : BaseC
|
||||||
|
|
||||||
|
<!ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED!>class DerivedC1<!> : BaseCImpl()
|
||||||
|
class DerivedC2 : BaseCImpl() {
|
||||||
|
override fun foo() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
expect interface BaseD {
|
||||||
|
fun foo()
|
||||||
|
}
|
||||||
|
abstract class BaseDImpl() : BaseD {
|
||||||
|
fun bar() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
expect interface BaseE {
|
||||||
|
fun foo()
|
||||||
|
}
|
||||||
|
sealed class BaseEImpl() : BaseE {
|
||||||
|
fun bar() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
|
||||||
|
}
|
||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public abstract expect class BaseA {
|
||||||
|
public constructor BaseA()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public abstract expect fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public open expect class BaseAImpl : BaseA {
|
||||||
|
public constructor BaseAImpl()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final expect override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public expect interface BaseB {
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public abstract expect fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public open expect class BaseBImpl : BaseB {
|
||||||
|
public constructor BaseBImpl()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final expect override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public expect interface BaseC {
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public abstract expect fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract expect class BaseCImpl : BaseC {
|
||||||
|
public constructor BaseCImpl()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public abstract expect override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public expect interface BaseD {
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public abstract expect fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class BaseDImpl : BaseD {
|
||||||
|
public constructor BaseDImpl()
|
||||||
|
public final fun bar(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public abstract expect override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public expect interface BaseE {
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public abstract expect fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class BaseEImpl : BaseE {
|
||||||
|
private constructor BaseEImpl()
|
||||||
|
public final fun bar(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public abstract expect override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class DerivedA1 : BaseAImpl {
|
||||||
|
public constructor DerivedA1()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final expect override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class DerivedA2 : BaseAImpl {
|
||||||
|
public constructor DerivedA2()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class DerivedB1 : BaseBImpl {
|
||||||
|
public constructor DerivedB1()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final expect override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class DerivedB2 : BaseBImpl {
|
||||||
|
public constructor DerivedB2()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class DerivedC1 : BaseCImpl {
|
||||||
|
public constructor DerivedC1()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public abstract expect override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class DerivedC2 : BaseCImpl {
|
||||||
|
public constructor DerivedC2()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
+1
-1
@@ -24,7 +24,7 @@ public final expect class DerivedExplicitCheck : Base {
|
|||||||
|
|
||||||
public final expect class DerivedImplicit : Base {
|
public final expect class DerivedImplicit : Base {
|
||||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
public abstract expect override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit
|
public final expect override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit
|
||||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14452,6 +14452,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("extendExpectedClassWithAbstractMember.kt")
|
||||||
|
public void testExtendExpectedClassWithAbstractMember() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithAbstractMember.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("extendExpectedClassWithoutExplicitOverrideOfMethod.kt")
|
@TestMetadata("extendExpectedClassWithoutExplicitOverrideOfMethod.kt")
|
||||||
public void testExtendExpectedClassWithoutExplicitOverrideOfMethod() throws Exception {
|
public void testExtendExpectedClassWithoutExplicitOverrideOfMethod() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithoutExplicitOverrideOfMethod.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithoutExplicitOverrideOfMethod.kt");
|
||||||
|
|||||||
Generated
+6
@@ -14452,6 +14452,12 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("extendExpectedClassWithAbstractMember.kt")
|
||||||
|
public void testExtendExpectedClassWithAbstractMember() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithAbstractMember.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("extendExpectedClassWithoutExplicitOverrideOfMethod.kt")
|
@TestMetadata("extendExpectedClassWithoutExplicitOverrideOfMethod.kt")
|
||||||
public void testExtendExpectedClassWithoutExplicitOverrideOfMethod() throws Exception {
|
public void testExtendExpectedClassWithoutExplicitOverrideOfMethod() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithoutExplicitOverrideOfMethod.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithoutExplicitOverrideOfMethod.kt");
|
||||||
|
|||||||
@@ -625,7 +625,7 @@ public class OverridingUtil {
|
|||||||
boolean allInvisible = visibleOverridables.isEmpty();
|
boolean allInvisible = visibleOverridables.isEmpty();
|
||||||
Collection<CallableMemberDescriptor> effectiveOverridden = allInvisible ? overridables : visibleOverridables;
|
Collection<CallableMemberDescriptor> effectiveOverridden = allInvisible ? overridables : visibleOverridables;
|
||||||
|
|
||||||
Modality modality = determineModality(effectiveOverridden);
|
Modality modality = determineModalityForFakeOverride(effectiveOverridden, current);
|
||||||
Visibility visibility = allInvisible ? Visibilities.INVISIBLE_FAKE : Visibilities.INHERITED;
|
Visibility visibility = allInvisible ? Visibilities.INVISIBLE_FAKE : Visibilities.INHERITED;
|
||||||
|
|
||||||
// FIXME doesn't work as expected for flexible types: should create a refined signature.
|
// FIXME doesn't work as expected for flexible types: should create a refined signature.
|
||||||
@@ -652,11 +652,16 @@ public class OverridingUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static Modality determineModality(@NotNull Collection<CallableMemberDescriptor> descriptors) {
|
private static Modality determineModalityForFakeOverride(
|
||||||
|
@NotNull Collection<CallableMemberDescriptor> descriptors,
|
||||||
|
@NotNull ClassDescriptor current
|
||||||
|
) {
|
||||||
// Optimization: avoid creating hash sets in frequent cases when modality can be computed trivially
|
// Optimization: avoid creating hash sets in frequent cases when modality can be computed trivially
|
||||||
boolean hasOpen = false;
|
boolean hasOpen = false;
|
||||||
boolean hasAbstract = false;
|
boolean hasAbstract = false;
|
||||||
|
boolean hasExpect = false;
|
||||||
for (CallableMemberDescriptor descriptor : descriptors) {
|
for (CallableMemberDescriptor descriptor : descriptors) {
|
||||||
|
hasExpect |= descriptor.isExpect();
|
||||||
switch (descriptor.getModality()) {
|
switch (descriptor.getModality()) {
|
||||||
case FINAL:
|
case FINAL:
|
||||||
return Modality.FINAL;
|
return Modality.FINAL;
|
||||||
@@ -671,25 +676,38 @@ public class OverridingUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasOpen && !hasAbstract) return Modality.OPEN;
|
if (!hasExpect) {
|
||||||
if (!hasOpen && hasAbstract) return Modality.ABSTRACT;
|
if (hasOpen && !hasAbstract) return Modality.OPEN;
|
||||||
|
if (!hasOpen && hasAbstract) return Modality.ABSTRACT;
|
||||||
|
}
|
||||||
|
|
||||||
Set<CallableMemberDescriptor> allOverriddenDeclarations = new HashSet<CallableMemberDescriptor>();
|
Set<CallableMemberDescriptor> allOverriddenDeclarations = new HashSet<CallableMemberDescriptor>();
|
||||||
for (CallableMemberDescriptor descriptor : descriptors) {
|
for (CallableMemberDescriptor descriptor : descriptors) {
|
||||||
allOverriddenDeclarations.addAll(getOverriddenDeclarations(descriptor));
|
allOverriddenDeclarations.addAll(getOverriddenDeclarations(descriptor));
|
||||||
}
|
}
|
||||||
return getMinimalModality(filterOutOverridden(allOverriddenDeclarations));
|
// Fake overrides of abstract members in non-abstract expected classes should not be abstract, because otherwise it would be
|
||||||
|
// impossible to inherit a non-expected class from that expected class in common code.
|
||||||
|
// We cannot assume that they're open though, because the actual abstract function from an expected super class
|
||||||
|
// can be implemented with a final function in the actual class for this class.
|
||||||
|
boolean transformAbstractToFinal =
|
||||||
|
current.isExpect() && (current.getModality() != Modality.ABSTRACT && current.getModality() != Modality.SEALED);
|
||||||
|
return getMinimalModality(filterOutOverridden(allOverriddenDeclarations), transformAbstractToFinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static Modality getMinimalModality(@NotNull Collection<CallableMemberDescriptor> descriptors) {
|
private static Modality getMinimalModality(
|
||||||
Modality modality = Modality.ABSTRACT;
|
@NotNull Collection<CallableMemberDescriptor> descriptors,
|
||||||
|
boolean transformAbstractToFinal
|
||||||
|
) {
|
||||||
|
Modality result = Modality.ABSTRACT;
|
||||||
for (CallableMemberDescriptor descriptor : descriptors) {
|
for (CallableMemberDescriptor descriptor : descriptors) {
|
||||||
if (descriptor.getModality().compareTo(modality) < 0) {
|
Modality effectiveModality =
|
||||||
modality = descriptor.getModality();
|
transformAbstractToFinal && descriptor.getModality() == Modality.ABSTRACT ? Modality.FINAL : descriptor.getModality();
|
||||||
|
if (effectiveModality.compareTo(result) < 0) {
|
||||||
|
result = effectiveModality;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return modality;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
Reference in New Issue
Block a user