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:
+31
@@ -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
|
||||
}
|
||||
+69
@@ -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
|
||||
}
|
||||
+7
@@ -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
|
||||
}
|
||||
+16
@@ -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
|
||||
}
|
||||
+10
@@ -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
|
||||
}
|
||||
+23
@@ -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
|
||||
}
|
||||
+2
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user