From 3151d4ca9dafc2a673496b56d565eda86b24f89b Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 19 Oct 2015 12:13:35 +0300 Subject: [PATCH] Check of type parameter bounds consistency #KT-9438 Fixed --- .../jetbrains/kotlin/diagnostics/Errors.java | 2 + .../rendering/DefaultErrorMessages.java | 1 + .../kotlin/resolve/DeclarationsChecker.java | 50 +++++++++---- .../tests/generics/TypeParameterBounds.kt | 29 ++++++++ .../tests/generics/TypeParameterBounds.txt | 71 +++++++++++++++++++ .../typeParameters/upperBoundCannotBeArray.kt | 4 +- .../checkers/JetDiagnosticsTestGenerated.java | 6 ++ idea/testData/checker/TypeParameterBounds.kt | 11 +++ .../checkers/JetPsiCheckerTestGenerated.java | 6 ++ 9 files changed, 166 insertions(+), 14 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/generics/TypeParameterBounds.kt create mode 100644 compiler/testData/diagnostics/tests/generics/TypeParameterBounds.txt create mode 100644 idea/testData/checker/TypeParameterBounds.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 68b75c1bb20..f0e52e5b125 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -182,6 +182,8 @@ public interface Errors { DiagnosticFactory0 SUPERTYPE_APPEARS_TWICE = DiagnosticFactory0.create(ERROR); DiagnosticFactory3> INCONSISTENT_TYPE_PARAMETER_VALUES = DiagnosticFactory3.create(ERROR); + DiagnosticFactory3> + INCONSISTENT_TYPE_PARAMETER_BOUNDS = DiagnosticFactory3.create(ERROR); DiagnosticFactory0 FINAL_SUPERTYPE = DiagnosticFactory0.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 48604b7315d..04c41833268 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -524,6 +524,7 @@ public class DefaultErrorMessages { MAP.put(UNCHECKED_CAST, "Unchecked cast: {0} to {1}", RENDER_TYPE, RENDER_TYPE); MAP.put(INCONSISTENT_TYPE_PARAMETER_VALUES, "Type parameter {0} of ''{1}'' has inconsistent values: {2}", NAME, NAME, RENDER_COLLECTION_OF_TYPES); + MAP.put(INCONSISTENT_TYPE_PARAMETER_BOUNDS, "Type parameter {0} of ''{1}'' has inconsistent bounds: {2}", NAME, NAME, RENDER_COLLECTION_OF_TYPES); MAP.put(EQUALITY_NOT_APPLICABLE, "Operator ''{0}'' cannot be applied to ''{1}'' and ''{2}''", new Renderer() { @NotNull diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java index 28ce3494056..ad9d0f249cc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java @@ -74,7 +74,7 @@ public class DeclarationsChecker { KtClassOrObject classOrObject = entry.getKey(); ClassDescriptorWithResolutionScopes classDescriptor = entry.getValue(); - checkSupertypesForConsistency(classDescriptor); + checkSupertypesForConsistency(classOrObject, classDescriptor); checkTypesInClassHeader(classOrObject); if (classOrObject instanceof KtClass) { @@ -184,9 +184,26 @@ public class DeclarationsChecker { } } - private void checkSupertypesForConsistency(@NotNull ClassDescriptor classDescriptor) { - Multimap multimap = SubstitutionUtils - .buildDeepSubstitutionMultimap(classDescriptor.getDefaultType()); + private void checkSupertypesForConsistency( + @NotNull KtClassOrObject classOrObject, + @NotNull ClassDescriptor classDescriptor + ) { + checkSupertypesForConsistency(classDescriptor, classOrObject); + } + + private void checkSupertypesForConsistency( + @NotNull KtTypeParameter typeParameter, + @NotNull TypeParameterDescriptor typeParameterDescriptor + ) { + checkSupertypesForConsistency(typeParameterDescriptor, typeParameter); + } + + private void checkSupertypesForConsistency( + @NotNull ClassifierDescriptor classifierDescriptor, + @NotNull PsiElement sourceElement + ) { + Multimap multimap = + SubstitutionUtils.buildDeepSubstitutionMultimap(classifierDescriptor.getDefaultType()); for (Map.Entry> entry : multimap.asMap().entrySet()) { Collection projections = entry.getValue(); if (projections.size() > 1) { @@ -204,14 +221,19 @@ public class DeclarationsChecker { if (conflictingTypes.size() > 1) { DeclarationDescriptor containingDeclaration = typeParameterDescriptor.getContainingDeclaration(); assert containingDeclaration instanceof ClassDescriptor : containingDeclaration; - KtClassOrObject psiElement = (KtClassOrObject) DescriptorToSourceUtils.getSourceFromDescriptor(classDescriptor); - assert psiElement != null; - KtDelegationSpecifierList delegationSpecifierList = psiElement.getDelegationSpecifierList(); - assert delegationSpecifierList != null; - // trace.getErrorHandler().genericError(delegationSpecifierList.getNode(), "Type parameter " + typeParameterDescriptor.getName() + " of " + containingDeclaration.getName() + " has inconsistent values: " + conflictingTypes); - trace.report(INCONSISTENT_TYPE_PARAMETER_VALUES - .on(delegationSpecifierList, typeParameterDescriptor, (ClassDescriptor) containingDeclaration, - conflictingTypes)); + if (sourceElement instanceof KtClassOrObject) { + KtDelegationSpecifierList delegationSpecifierList = ((KtClassOrObject) sourceElement).getDelegationSpecifierList(); + assert delegationSpecifierList != null; + // trace.getErrorHandler().genericError(delegationSpecifierList.getNode(), "Type parameter " + typeParameterDescriptor.getName() + " of " + containingDeclaration.getName() + " has inconsistent values: " + conflictingTypes); + trace.report(INCONSISTENT_TYPE_PARAMETER_VALUES + .on(delegationSpecifierList, typeParameterDescriptor, (ClassDescriptor) containingDeclaration, + conflictingTypes)); + } + else if (sourceElement instanceof KtTypeParameter) { + trace.report(INCONSISTENT_TYPE_PARAMETER_BOUNDS + .on((KtTypeParameter) sourceElement, typeParameterDescriptor, (ClassDescriptor) containingDeclaration, + conflictingTypes)); + } } } } @@ -362,6 +384,10 @@ public class DeclarationsChecker { if (typeParameter.getExtendsBound() != null && hasConstraints(typeParameter, constraints)) { trace.report(MISPLACED_TYPE_PARAMETER_CONSTRAINTS.on(typeParameter)); } + TypeParameterDescriptor descriptor = trace.get(TYPE_PARAMETER, typeParameter); + if (descriptor != null) { + checkSupertypesForConsistency(typeParameter, descriptor); + } } } } diff --git a/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.kt b/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.kt new file mode 100644 index 00000000000..324f74bfafd --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.kt @@ -0,0 +1,29 @@ +// See KT-9438: Enforce the Single Instantiation Inheritance Rule for type parameters + +interface A + +interface B + +interface D + +interface IncorrectF<T : D> where T : D + +interface CorrectF<T> where T : D, T : D + +interface G + +interface IncorrectH<T : G>> where T : G> + +interface CorrectH<T> where T : G>, T : G> + +interface I>> { + fun <S : T?> incorrectFoo() where S : G> + + fun <S> correctFoo() where S : T?, S : G> +} + +interface incorrectJ<T: G>> where T : G> + +interface correctJ<T> where T : G>, T : G> + +fun <T> bar() where T : D, T : D {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.txt b/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.txt new file mode 100644 index 00000000000..387b6f18db0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.txt @@ -0,0 +1,71 @@ +package + +public fun > bar(): kotlin.Unit where T : D + +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 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 CorrectF> where T : D { + 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 CorrectH>> where T : G> { + 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 D { + 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 G { + 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 I>> { + public abstract fun correctFoo(): kotlin.Unit where S : G> + 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 abstract fun incorrectFoo(): kotlin.Unit where S : G> + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface IncorrectF> where T : D { + 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 IncorrectH>> where T : G> { + 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 correctJ>> where T : G> { + 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 incorrectJ>> where T : G> { + 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/typeParameters/upperBoundCannotBeArray.kt b/compiler/testData/diagnostics/tests/typeParameters/upperBoundCannotBeArray.kt index 62cc753059a..21e69a75e99 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/upperBoundCannotBeArray.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/upperBoundCannotBeArray.kt @@ -1,6 +1,6 @@ fun <A : Array> f1() {} fun A : Array> f2() {} -fun A> f3() where A : Array, A : Array {} +fun A> f3() where A : Array, A : Array {} fun <T : IntArray> f4() {} @@ -9,7 +9,7 @@ fun <T> f5() where T : Array {} val <T : Array> T.v: String get() = "" class C2A : Array> -interface C3A> where A : Array, A : Array +interface C3A> where A : Array, A : Array fun foo() { class C1<A : Array> { diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index b1d58932280..4835dc51a1f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -6582,6 +6582,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("TypeParameterBounds.kt") + public void testTypeParameterBounds() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/TypeParameterBounds.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/diagnostics/tests/generics/nullability") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/testData/checker/TypeParameterBounds.kt b/idea/testData/checker/TypeParameterBounds.kt new file mode 100644 index 00000000000..237678bb9a8 --- /dev/null +++ b/idea/testData/checker/TypeParameterBounds.kt @@ -0,0 +1,11 @@ +// See KT-9438: Enforce the Single Instantiation Inheritance Rule for type parameters + +interface A + +interface B + +interface D + +interface CorrectF<T> where T : D, T : D + +fun <T> bar() where T : D, T : D {} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/checkers/JetPsiCheckerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/checkers/JetPsiCheckerTestGenerated.java index 46057ccce06..05f7c1c29d7 100644 --- a/idea/tests/org/jetbrains/kotlin/checkers/JetPsiCheckerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/checkers/JetPsiCheckerTestGenerated.java @@ -325,6 +325,12 @@ public class JetPsiCheckerTestGenerated extends AbstractJetPsiCheckerTest { doTest(fileName); } + @TestMetadata("TypeParameterBounds.kt") + public void testTypeParameterBounds() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/checker/TypeParameterBounds.kt"); + doTest(fileName); + } + @TestMetadata("UnreachableCode.kt") public void testUnreachableCode() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/checker/UnreachableCode.kt");