Change override rules when type parameters have multiple bounds

Instead of constructing the magic intersection type, compare sets of upper
bounds instead as javac does, modulo the substitution
This commit is contained in:
Alexander Udalov
2015-10-22 22:40:26 +03:00
parent a59ca2ea9f
commit 477823b093
9 changed files with 244 additions and 38 deletions
@@ -0,0 +1,31 @@
interface I1
interface I2
open class C
interface A {
fun <T> foo(t: T) where T : I1, T : C, T : I2
}
interface B1 : A {
override fun <T> foo(t: T) where T : C, T : I1, T : I2
}
interface B2 : A {
override fun <T> foo(t: T) where T : I1, T : C, T : I2
}
interface B3 : A {
override fun <T> foo(t: T) where T : I1, T : I2, T : C
}
interface B4 : A {
override fun <T> foo(t: T) where T : C, T : I2, T : I1
}
interface B5 : A {
override fun <T> foo(t: T) where T : I2, T : C, T : I1
}
interface B6 : A {
override fun <T> foo(t: T) where T : I2, T : I1, T : C
}
@@ -0,0 +1,69 @@
package
public interface A {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun </*0*/ T : I1> foo(/*0*/ t: T): kotlin.Unit where T : C, T : I2
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface B1 : A {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ fun </*0*/ T : C> foo(/*0*/ t: T): kotlin.Unit where T : I1, T : I2
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface B2 : A {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ fun </*0*/ T : I1> foo(/*0*/ t: T): kotlin.Unit where T : C, T : I2
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface B3 : A {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ fun </*0*/ T : I1> foo(/*0*/ t: T): kotlin.Unit where T : I2, T : C
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface B4 : A {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ fun </*0*/ T : C> foo(/*0*/ t: T): kotlin.Unit where T : I2, T : I1
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface B5 : A {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ fun </*0*/ T : I2> foo(/*0*/ t: T): kotlin.Unit where T : C, T : I1
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface B6 : A {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ fun </*0*/ T : I2> foo(/*0*/ t: T): kotlin.Unit where T : I1, T : C
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public open class C {
public constructor C()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface I1 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface I2 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,7 @@
interface A {
fun <T> foo() where T : Any, T : Cloneable?
}
interface B : A {
<!NOTHING_TO_OVERRIDE!>override<!> fun <T> foo() where T : Any?, T : Cloneable
}
@@ -0,0 +1,16 @@
package
public interface A {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun </*0*/ T : kotlin.Any> foo(): kotlin.Unit where T : kotlin.Cloneable?
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface B : A {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun </*0*/ T> foo(): kotlin.Unit where T : kotlin.Cloneable
public abstract override /*1*/ /*fake_override*/ fun </*0*/ T : kotlin.Any> foo(): kotlin.Unit where T : kotlin.Cloneable?
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,10 @@
interface A<R>
interface B {
fun accept(visitor: String)
fun <R> accept(visitor: A<R>): R
}
interface C : B {
override fun <R> accept(visitor: A<R>): R
}
@@ -0,0 +1,23 @@
package
public interface A</*0*/ R> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface B {
public abstract fun </*0*/ R> accept(/*0*/ visitor: A<R>): R
public abstract fun accept(/*0*/ visitor: kotlin.String): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface C : B {
public abstract override /*1*/ fun </*0*/ R> accept(/*0*/ visitor: A<R>): R
public abstract override /*1*/ /*fake_override*/ fun accept(/*0*/ visitor: kotlin.String): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -7,6 +7,7 @@ public/*package*/ open class NoSamForTypeParameter {
public/*package*/ open class NoSamForTypeParameterDerived1 : test.NoSamForTypeParameter {
public/*package*/ constructor NoSamForTypeParameterDerived1()
public/*package*/ open override /*1*/ /*fake_override*/ fun </*0*/ K : java.lang.Runnable!> foo(/*0*/ p0: K!, /*1*/ p1: java.lang.Runnable!): kotlin.Unit
public/*package*/ open fun foo(/*0*/ p0: java.lang.Runnable!, /*1*/ p1: java.lang.Runnable!): kotlin.Unit
}
@@ -17,5 +18,6 @@ public/*package*/ open class NoSamForTypeParameterDerived2 : test.NoSamForTypePa
public/*package*/ open class NoSamForTypeParameterDerived3 : test.NoSamForTypeParameterDerived1 {
public/*package*/ constructor NoSamForTypeParameterDerived3()
public/*package*/ open override /*1*/ /*fake_override*/ fun </*0*/ K : java.lang.Runnable!> foo(/*0*/ p0: K!, /*1*/ p1: java.lang.Runnable!): kotlin.Unit
public/*package*/ open override /*1*/ fun foo(/*0*/ p0: java.lang.Runnable!, /*1*/ p1: java.lang.Runnable!): kotlin.Unit
}
@@ -11033,6 +11033,33 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
}
@TestMetadata("compiler/testData/diagnostics/tests/override/typeParameters")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class TypeParameters extends AbstractJetDiagnosticsTest {
public void testAllFilesPresentInTypeParameters() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/override/typeParameters"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("classAndTwoInterfaceBounds.kt")
public void testClassAndTwoInterfaceBounds() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/override/typeParameters/classAndTwoInterfaceBounds.kt");
doTest(fileName);
}
@TestMetadata("differentSetsOfBounds.kt")
public void testDifferentSetsOfBounds() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/override/typeParameters/differentSetsOfBounds.kt");
doTest(fileName);
}
@TestMetadata("simpleVisitorTwoAccepts.kt")
public void testSimpleVisitorTwoAccepts() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/override/typeParameters/simpleVisitorTwoAccepts.kt");
doTest(fileName);
}
}
}
@TestMetadata("compiler/testData/diagnostics/tests/platformTypes")
@@ -105,51 +105,24 @@ public class OverridingUtil {
if (superTypeParameters.size() != subTypeParameters.size()) {
for (int i = 0; i < superValueParameters.size(); ++i) {
KotlinType superValueParameterType = getUpperBound(superValueParameters.get(i));
KotlinType subValueParameterType = getUpperBound(subValueParameters.get(i));
// TODO: compare erasure
if (!KotlinTypeChecker.DEFAULT.equalTypes(superValueParameterType, subValueParameterType)) {
if (!KotlinTypeChecker.DEFAULT.equalTypes(superValueParameters.get(i), subValueParameters.get(i))) {
return OverrideCompatibilityInfo.incompatible("Type parameter number mismatch");
}
}
return OverrideCompatibilityInfo.conflict("Type parameter number mismatch");
}
final Map<TypeConstructor, TypeConstructor> matchingTypeConstructors = new HashMap<TypeConstructor, TypeConstructor>();
for (int i = 0, typeParametersSize = superTypeParameters.size(); i < typeParametersSize; i++) {
TypeParameterDescriptor superTypeParameter = superTypeParameters.get(i);
TypeParameterDescriptor subTypeParameter = subTypeParameters.get(i);
matchingTypeConstructors.put(superTypeParameter.getTypeConstructor(), subTypeParameter.getTypeConstructor());
}
KotlinTypeChecker typeChecker = createTypeChecker(superTypeParameters, subTypeParameters);
KotlinTypeChecker.TypeConstructorEquality localEqualityAxioms = new KotlinTypeChecker.TypeConstructorEquality() {
@Override
public boolean equals(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
if (equalityAxioms.equals(a, b)) return true;
TypeConstructor img1 = matchingTypeConstructors.get(a);
TypeConstructor img2 = matchingTypeConstructors.get(b);
if (!(img1 != null && img1.equals(b)) &&
!(img2 != null && img2.equals(a))) {
return false;
}
return true;
}
};
for (int i = 0, typeParametersSize = superTypeParameters.size(); i < typeParametersSize; i++) {
TypeParameterDescriptor superTypeParameter = superTypeParameters.get(i);
TypeParameterDescriptor subTypeParameter = subTypeParameters.get(i);
if (!areTypesEquivalent(superTypeParameter.getUpperBoundsAsType(), subTypeParameter.getUpperBoundsAsType(), localEqualityAxioms)) {
for (int i = 0; i < superTypeParameters.size(); i++) {
if (!areTypeParametersEquivalent(superTypeParameters.get(i), subTypeParameters.get(i), typeChecker)) {
return OverrideCompatibilityInfo.incompatible("Type parameter bounds mismatch");
}
}
for (int i = 0, unsubstitutedValueParametersSize = superValueParameters.size(); i < unsubstitutedValueParametersSize; i++) {
KotlinType superValueParameter = superValueParameters.get(i);
KotlinType subValueParameter = subValueParameters.get(i);
if (!areTypesEquivalent(superValueParameter, subValueParameter, localEqualityAxioms)) {
for (int i = 0; i < superValueParameters.size(); i++) {
if (!areTypesEquivalent(superValueParameters.get(i), subValueParameters.get(i), typeChecker)) {
return OverrideCompatibilityInfo.incompatible("Value parameter type mismatch");
}
}
@@ -160,13 +133,12 @@ public class OverridingUtil {
if (superReturnType != null && subReturnType != null) {
boolean bothErrors = subReturnType.isError() && superReturnType.isError();
if (!bothErrors && !KotlinTypeChecker.withAxioms(localEqualityAxioms).isSubtypeOf(subReturnType, superReturnType)) {
if (!bothErrors && !typeChecker.isSubtypeOf(subReturnType, superReturnType)) {
return OverrideCompatibilityInfo.conflict("Return type mismatch");
}
}
}
for (ExternalOverridabilityCondition externalCondition : EXTERNAL_CONDITIONS) {
if (!externalCondition.isOverridable(superDescriptor, subDescriptor)) {
return OverrideCompatibilityInfo.incompatible("External condition failed");
@@ -176,6 +148,31 @@ public class OverridingUtil {
return OverrideCompatibilityInfo.success();
}
@NotNull
private KotlinTypeChecker createTypeChecker(
@NotNull List<TypeParameterDescriptor> firstParameters,
@NotNull List<TypeParameterDescriptor> secondParameters
) {
assert firstParameters.size() == secondParameters.size() :
"Should be the same number of type parameters: " + firstParameters + " vs " + secondParameters;
if (firstParameters.isEmpty()) return KotlinTypeChecker.withAxioms(equalityAxioms);
final Map<TypeConstructor, TypeConstructor> matchingTypeConstructors = new HashMap<TypeConstructor, TypeConstructor>();
for (int i = 0; i < firstParameters.size(); i++) {
matchingTypeConstructors.put(firstParameters.get(i).getTypeConstructor(), secondParameters.get(i).getTypeConstructor());
}
return KotlinTypeChecker.withAxioms(new KotlinTypeChecker.TypeConstructorEquality() {
@Override
public boolean equals(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
if (equalityAxioms.equals(a, b)) return true;
TypeConstructor img1 = matchingTypeConstructors.get(a);
TypeConstructor img2 = matchingTypeConstructors.get(b);
return (img1 != null && img1.equals(b)) || (img2 != null && img2.equals(a));
}
});
}
@Nullable
static OverrideCompatibilityInfo checkReceiverAndParameterCount(
CallableDescriptor superDescriptor,
@@ -195,12 +192,36 @@ public class OverridingUtil {
private static boolean areTypesEquivalent(
@NotNull KotlinType typeInSuper,
@NotNull KotlinType typeInSub,
@NotNull KotlinTypeChecker.TypeConstructorEquality axioms
@NotNull KotlinTypeChecker typeChecker
) {
boolean bothErrors = typeInSuper.isError() && typeInSub.isError();
if (!bothErrors && !KotlinTypeChecker.withAxioms(axioms).equalTypes(typeInSuper, typeInSub)) {
return bothErrors || typeChecker.equalTypes(typeInSuper, typeInSub);
}
// See JLS 8, §8.4.4 Generic Methods
// TODO: use TypeSubstitutor instead
private static boolean areTypeParametersEquivalent(
@NotNull TypeParameterDescriptor superTypeParameter,
@NotNull TypeParameterDescriptor subTypeParameter,
@NotNull KotlinTypeChecker typeChecker
) {
List<KotlinType> superBounds = superTypeParameter.getUpperBounds();
List<KotlinType> subBounds = new ArrayList<KotlinType>(subTypeParameter.getUpperBounds());
if (superBounds.size() != subBounds.size()) return false;
outer:
for (KotlinType superBound : superBounds) {
ListIterator<KotlinType> it = subBounds.listIterator();
while (it.hasNext()) {
KotlinType subBound = it.next();
if (areTypesEquivalent(superBound, subBound, typeChecker)) {
it.remove();
continue outer;
}
}
return false;
}
return true;
}
@@ -532,7 +553,7 @@ public class OverridingUtil {
CONFLICT,
}
private static final OverrideCompatibilityInfo SUCCESS = new OverrideCompatibilityInfo(Result.OVERRIDABLE, "SUCCESS");
private static final OverrideCompatibilityInfo SUCCESS = new OverrideCompatibilityInfo(OVERRIDABLE, "SUCCESS");
@NotNull
public static OverrideCompatibilityInfo success() {