diff --git a/compiler/testData/diagnostics/tests/override/typeParameters/classAndTwoInterfaceBounds.kt b/compiler/testData/diagnostics/tests/override/typeParameters/classAndTwoInterfaceBounds.kt new file mode 100644 index 00000000000..61f5cf91a8c --- /dev/null +++ b/compiler/testData/diagnostics/tests/override/typeParameters/classAndTwoInterfaceBounds.kt @@ -0,0 +1,31 @@ +interface I1 +interface I2 +open class C + +interface A { + fun foo(t: T) where T : I1, T : C, T : I2 +} + +interface B1 : A { + override fun foo(t: T) where T : C, T : I1, T : I2 +} + +interface B2 : A { + override fun foo(t: T) where T : I1, T : C, T : I2 +} + +interface B3 : A { + override fun foo(t: T) where T : I1, T : I2, T : C +} + +interface B4 : A { + override fun foo(t: T) where T : C, T : I2, T : I1 +} + +interface B5 : A { + override fun foo(t: T) where T : I2, T : C, T : I1 +} + +interface B6 : A { + override fun foo(t: T) where T : I2, T : I1, T : C +} diff --git a/compiler/testData/diagnostics/tests/override/typeParameters/classAndTwoInterfaceBounds.txt b/compiler/testData/diagnostics/tests/override/typeParameters/classAndTwoInterfaceBounds.txt new file mode 100644 index 00000000000..617c24d5f3d --- /dev/null +++ b/compiler/testData/diagnostics/tests/override/typeParameters/classAndTwoInterfaceBounds.txt @@ -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 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 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 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 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 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 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 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 +} diff --git a/compiler/testData/diagnostics/tests/override/typeParameters/differentSetsOfBounds.kt b/compiler/testData/diagnostics/tests/override/typeParameters/differentSetsOfBounds.kt new file mode 100644 index 00000000000..f048e5cf395 --- /dev/null +++ b/compiler/testData/diagnostics/tests/override/typeParameters/differentSetsOfBounds.kt @@ -0,0 +1,7 @@ +interface A { + fun foo() where T : Any, T : Cloneable? +} + +interface B : A { + override fun foo() where T : Any?, T : Cloneable +} diff --git a/compiler/testData/diagnostics/tests/override/typeParameters/differentSetsOfBounds.txt b/compiler/testData/diagnostics/tests/override/typeParameters/differentSetsOfBounds.txt new file mode 100644 index 00000000000..4b8f4eca93f --- /dev/null +++ b/compiler/testData/diagnostics/tests/override/typeParameters/differentSetsOfBounds.txt @@ -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 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 foo(): kotlin.Unit where T : kotlin.Cloneable + public abstract override /*1*/ /*fake_override*/ fun 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 +} diff --git a/compiler/testData/diagnostics/tests/override/typeParameters/simpleVisitorTwoAccepts.kt b/compiler/testData/diagnostics/tests/override/typeParameters/simpleVisitorTwoAccepts.kt new file mode 100644 index 00000000000..37d3705f995 --- /dev/null +++ b/compiler/testData/diagnostics/tests/override/typeParameters/simpleVisitorTwoAccepts.kt @@ -0,0 +1,10 @@ +interface A + +interface B { + fun accept(visitor: String) + fun accept(visitor: A): R +} + +interface C : B { + override fun accept(visitor: A): R +} diff --git a/compiler/testData/diagnostics/tests/override/typeParameters/simpleVisitorTwoAccepts.txt b/compiler/testData/diagnostics/tests/override/typeParameters/simpleVisitorTwoAccepts.txt new file mode 100644 index 00000000000..7f2def5ea23 --- /dev/null +++ b/compiler/testData/diagnostics/tests/override/typeParameters/simpleVisitorTwoAccepts.txt @@ -0,0 +1,23 @@ +package + +public interface A { + 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 accept(/*0*/ visitor: A): 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 accept(/*0*/ visitor: A): 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 +} diff --git a/compiler/testData/loadJava/compiledJava/sam/adapters/NoSamForMethodTypeParameter.txt b/compiler/testData/loadJava/compiledJava/sam/adapters/NoSamForMethodTypeParameter.txt index f6376c68bd4..72bb46cc9e0 100644 --- a/compiler/testData/loadJava/compiledJava/sam/adapters/NoSamForMethodTypeParameter.txt +++ b/compiler/testData/loadJava/compiledJava/sam/adapters/NoSamForMethodTypeParameter.txt @@ -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 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 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 } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index e5bab456612..81217a39da6 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -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") diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java index fd916b2abe2..4a4bd6a674f 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java @@ -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 matchingTypeConstructors = new HashMap(); - 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 firstParameters, + @NotNull List 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 matchingTypeConstructors = new HashMap(); + 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 superBounds = superTypeParameter.getUpperBounds(); + List subBounds = new ArrayList(subTypeParameter.getUpperBounds()); + if (superBounds.size() != subBounds.size()) return false; + + outer: + for (KotlinType superBound : superBounds) { + ListIterator 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() {