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:
Alexander Udalov
2018-01-09 12:39:19 +01:00
parent 88a23c73c7
commit 56be83cdd3
6 changed files with 223 additions and 11 deletions
@@ -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<!>()
}
@@ -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
}
@@ -24,7 +24,7 @@ public final expect class DerivedExplicitCheck : Base {
public final expect class DerivedImplicit : Base {
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 toString(): kotlin.String
}
@@ -14452,6 +14452,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
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")
public void testExtendExpectedClassWithoutExplicitOverrideOfMethod() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithoutExplicitOverrideOfMethod.kt");
@@ -14452,6 +14452,12 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
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")
public void testExtendExpectedClassWithoutExplicitOverrideOfMethod() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithoutExplicitOverrideOfMethod.kt");
@@ -625,7 +625,7 @@ public class OverridingUtil {
boolean allInvisible = visibleOverridables.isEmpty();
Collection<CallableMemberDescriptor> effectiveOverridden = allInvisible ? overridables : visibleOverridables;
Modality modality = determineModality(effectiveOverridden);
Modality modality = determineModalityForFakeOverride(effectiveOverridden, current);
Visibility visibility = allInvisible ? Visibilities.INVISIBLE_FAKE : Visibilities.INHERITED;
// FIXME doesn't work as expected for flexible types: should create a refined signature.
@@ -652,11 +652,16 @@ public class OverridingUtil {
}
@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
boolean hasOpen = false;
boolean hasAbstract = false;
boolean hasExpect = false;
for (CallableMemberDescriptor descriptor : descriptors) {
hasExpect |= descriptor.isExpect();
switch (descriptor.getModality()) {
case FINAL:
return Modality.FINAL;
@@ -671,25 +676,38 @@ public class OverridingUtil {
}
}
if (hasOpen && !hasAbstract) return Modality.OPEN;
if (!hasOpen && hasAbstract) return Modality.ABSTRACT;
if (!hasExpect) {
if (hasOpen && !hasAbstract) return Modality.OPEN;
if (!hasOpen && hasAbstract) return Modality.ABSTRACT;
}
Set<CallableMemberDescriptor> allOverriddenDeclarations = new HashSet<CallableMemberDescriptor>();
for (CallableMemberDescriptor descriptor : descriptors) {
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
private static Modality getMinimalModality(@NotNull Collection<CallableMemberDescriptor> descriptors) {
Modality modality = Modality.ABSTRACT;
private static Modality getMinimalModality(
@NotNull Collection<CallableMemberDescriptor> descriptors,
boolean transformAbstractToFinal
) {
Modality result = Modality.ABSTRACT;
for (CallableMemberDescriptor descriptor : descriptors) {
if (descriptor.getModality().compareTo(modality) < 0) {
modality = descriptor.getModality();
Modality effectiveModality =
transformAbstractToFinal && descriptor.getModality() == Modality.ABSTRACT ? Modality.FINAL : descriptor.getModality();
if (effectiveModality.compareTo(result) < 0) {
result = effectiveModality;
}
}
return modality;
return result;
}
@NotNull