Check of type parameter bounds consistency #KT-9438 Fixed
This commit is contained in:
@@ -182,6 +182,8 @@ public interface Errors {
|
|||||||
DiagnosticFactory0<KtTypeReference> SUPERTYPE_APPEARS_TWICE = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtTypeReference> SUPERTYPE_APPEARS_TWICE = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory3<KtDelegationSpecifierList, TypeParameterDescriptor, ClassDescriptor, Collection<KtType>>
|
DiagnosticFactory3<KtDelegationSpecifierList, TypeParameterDescriptor, ClassDescriptor, Collection<KtType>>
|
||||||
INCONSISTENT_TYPE_PARAMETER_VALUES = DiagnosticFactory3.create(ERROR);
|
INCONSISTENT_TYPE_PARAMETER_VALUES = DiagnosticFactory3.create(ERROR);
|
||||||
|
DiagnosticFactory3<KtTypeParameter, TypeParameterDescriptor, ClassDescriptor, Collection<KtType>>
|
||||||
|
INCONSISTENT_TYPE_PARAMETER_BOUNDS = DiagnosticFactory3.create(ERROR);
|
||||||
|
|
||||||
|
|
||||||
DiagnosticFactory0<KtTypeReference> FINAL_SUPERTYPE = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtTypeReference> FINAL_SUPERTYPE = DiagnosticFactory0.create(ERROR);
|
||||||
|
|||||||
+1
@@ -524,6 +524,7 @@ public class DefaultErrorMessages {
|
|||||||
MAP.put(UNCHECKED_CAST, "Unchecked cast: {0} to {1}", RENDER_TYPE, RENDER_TYPE);
|
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_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<KtSimpleNameExpression>() {
|
MAP.put(EQUALITY_NOT_APPLICABLE, "Operator ''{0}'' cannot be applied to ''{1}'' and ''{2}''", new Renderer<KtSimpleNameExpression>() {
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ public class DeclarationsChecker {
|
|||||||
KtClassOrObject classOrObject = entry.getKey();
|
KtClassOrObject classOrObject = entry.getKey();
|
||||||
ClassDescriptorWithResolutionScopes classDescriptor = entry.getValue();
|
ClassDescriptorWithResolutionScopes classDescriptor = entry.getValue();
|
||||||
|
|
||||||
checkSupertypesForConsistency(classDescriptor);
|
checkSupertypesForConsistency(classOrObject, classDescriptor);
|
||||||
checkTypesInClassHeader(classOrObject);
|
checkTypesInClassHeader(classOrObject);
|
||||||
|
|
||||||
if (classOrObject instanceof KtClass) {
|
if (classOrObject instanceof KtClass) {
|
||||||
@@ -184,9 +184,26 @@ public class DeclarationsChecker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkSupertypesForConsistency(@NotNull ClassDescriptor classDescriptor) {
|
private void checkSupertypesForConsistency(
|
||||||
Multimap<TypeConstructor, TypeProjection> multimap = SubstitutionUtils
|
@NotNull KtClassOrObject classOrObject,
|
||||||
.buildDeepSubstitutionMultimap(classDescriptor.getDefaultType());
|
@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<TypeConstructor, TypeProjection> multimap =
|
||||||
|
SubstitutionUtils.buildDeepSubstitutionMultimap(classifierDescriptor.getDefaultType());
|
||||||
for (Map.Entry<TypeConstructor, Collection<TypeProjection>> entry : multimap.asMap().entrySet()) {
|
for (Map.Entry<TypeConstructor, Collection<TypeProjection>> entry : multimap.asMap().entrySet()) {
|
||||||
Collection<TypeProjection> projections = entry.getValue();
|
Collection<TypeProjection> projections = entry.getValue();
|
||||||
if (projections.size() > 1) {
|
if (projections.size() > 1) {
|
||||||
@@ -204,14 +221,19 @@ public class DeclarationsChecker {
|
|||||||
if (conflictingTypes.size() > 1) {
|
if (conflictingTypes.size() > 1) {
|
||||||
DeclarationDescriptor containingDeclaration = typeParameterDescriptor.getContainingDeclaration();
|
DeclarationDescriptor containingDeclaration = typeParameterDescriptor.getContainingDeclaration();
|
||||||
assert containingDeclaration instanceof ClassDescriptor : containingDeclaration;
|
assert containingDeclaration instanceof ClassDescriptor : containingDeclaration;
|
||||||
KtClassOrObject psiElement = (KtClassOrObject) DescriptorToSourceUtils.getSourceFromDescriptor(classDescriptor);
|
if (sourceElement instanceof KtClassOrObject) {
|
||||||
assert psiElement != null;
|
KtDelegationSpecifierList delegationSpecifierList = ((KtClassOrObject) sourceElement).getDelegationSpecifierList();
|
||||||
KtDelegationSpecifierList delegationSpecifierList = psiElement.getDelegationSpecifierList();
|
assert delegationSpecifierList != null;
|
||||||
assert delegationSpecifierList != null;
|
// trace.getErrorHandler().genericError(delegationSpecifierList.getNode(), "Type parameter " + typeParameterDescriptor.getName() + " of " + containingDeclaration.getName() + " has inconsistent values: " + conflictingTypes);
|
||||||
// trace.getErrorHandler().genericError(delegationSpecifierList.getNode(), "Type parameter " + typeParameterDescriptor.getName() + " of " + containingDeclaration.getName() + " has inconsistent values: " + conflictingTypes);
|
trace.report(INCONSISTENT_TYPE_PARAMETER_VALUES
|
||||||
trace.report(INCONSISTENT_TYPE_PARAMETER_VALUES
|
.on(delegationSpecifierList, typeParameterDescriptor, (ClassDescriptor) containingDeclaration,
|
||||||
.on(delegationSpecifierList, typeParameterDescriptor, (ClassDescriptor) containingDeclaration,
|
conflictingTypes));
|
||||||
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)) {
|
if (typeParameter.getExtendsBound() != null && hasConstraints(typeParameter, constraints)) {
|
||||||
trace.report(MISPLACED_TYPE_PARAMETER_CONSTRAINTS.on(typeParameter));
|
trace.report(MISPLACED_TYPE_PARAMETER_CONSTRAINTS.on(typeParameter));
|
||||||
}
|
}
|
||||||
|
TypeParameterDescriptor descriptor = trace.get(TYPE_PARAMETER, typeParameter);
|
||||||
|
if (descriptor != null) {
|
||||||
|
checkSupertypesForConsistency(typeParameter, descriptor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// See KT-9438: Enforce the Single Instantiation Inheritance Rule for type parameters
|
||||||
|
|
||||||
|
interface A
|
||||||
|
|
||||||
|
interface B
|
||||||
|
|
||||||
|
interface D<T>
|
||||||
|
|
||||||
|
interface IncorrectF<<!MISPLACED_TYPE_PARAMETER_CONSTRAINTS, INCONSISTENT_TYPE_PARAMETER_BOUNDS!>T : D<A><!>> where T : D<B>
|
||||||
|
|
||||||
|
interface CorrectF<<!INCONSISTENT_TYPE_PARAMETER_BOUNDS!>T<!>> where T : D<A>, T : D<B>
|
||||||
|
|
||||||
|
interface G<T>
|
||||||
|
|
||||||
|
interface IncorrectH<<!MISPLACED_TYPE_PARAMETER_CONSTRAINTS, INCONSISTENT_TYPE_PARAMETER_BOUNDS!>T : G<D<A>><!>> where T : G<D<T>>
|
||||||
|
|
||||||
|
interface CorrectH<<!INCONSISTENT_TYPE_PARAMETER_BOUNDS!>T<!>> where T : G<D<A>>, T : G<D<B>>
|
||||||
|
|
||||||
|
interface I<T : G<D<T>>> {
|
||||||
|
fun <<!MISPLACED_TYPE_PARAMETER_CONSTRAINTS, INCONSISTENT_TYPE_PARAMETER_BOUNDS!>S : T?<!>> incorrectFoo() where S : G<D<S>>
|
||||||
|
|
||||||
|
fun <<!INCONSISTENT_TYPE_PARAMETER_BOUNDS!>S<!>> correctFoo() where S : T?, S : G<D<S>>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface incorrectJ<<!MISPLACED_TYPE_PARAMETER_CONSTRAINTS, INCONSISTENT_TYPE_PARAMETER_BOUNDS!>T: G<D<T>><!>> where T : G<D<T?>>
|
||||||
|
|
||||||
|
interface correctJ<<!INCONSISTENT_TYPE_PARAMETER_BOUNDS!>T<!>> where T : G<D<T>>, T : G<D<T?>>
|
||||||
|
|
||||||
|
fun <<!INCONSISTENT_TYPE_PARAMETER_BOUNDS!>T<!>> bar() where T : D<A>, T : D<B> {}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun </*0*/ T : D<A>> bar(): kotlin.Unit where T : D<B>
|
||||||
|
|
||||||
|
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</*0*/ T : D<A>> where T : D<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 CorrectH</*0*/ T : G<D<A>>> where T : G<D<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 D</*0*/ T> {
|
||||||
|
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</*0*/ T> {
|
||||||
|
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</*0*/ T : G<D<T>>> {
|
||||||
|
public abstract fun </*0*/ S : T?> correctFoo(): kotlin.Unit where S : G<D<S>>
|
||||||
|
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 </*0*/ S : T?> incorrectFoo(): kotlin.Unit where S : G<D<S>>
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IncorrectF</*0*/ T : D<A>> where T : D<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 IncorrectH</*0*/ T : G<D<A>>> where T : G<D<T>> {
|
||||||
|
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</*0*/ T : G<D<T>>> where T : G<D<T?>> {
|
||||||
|
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</*0*/ T : G<D<T>>> where T : G<D<T?>> {
|
||||||
|
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
-2
@@ -1,6 +1,6 @@
|
|||||||
fun <<!UPPER_BOUND_CANNOT_BE_ARRAY!>A : Array<Any><!>> f1() {}
|
fun <<!UPPER_BOUND_CANNOT_BE_ARRAY!>A : Array<Any><!>> f1() {}
|
||||||
fun <T, <!UPPER_BOUND_CANNOT_BE_ARRAY!>A : Array<out T><!>> f2() {}
|
fun <T, <!UPPER_BOUND_CANNOT_BE_ARRAY!>A : Array<out T><!>> f2() {}
|
||||||
fun <S, T : S, <!UPPER_BOUND_CANNOT_BE_ARRAY!>A<!>> f3() where A : Array<out S>, A : Array<out T> {}
|
fun <S, T : S, <!INCONSISTENT_TYPE_PARAMETER_BOUNDS, UPPER_BOUND_CANNOT_BE_ARRAY!>A<!>> f3() where A : Array<out S>, A : Array<out T> {}
|
||||||
|
|
||||||
fun <<!UPPER_BOUND_CANNOT_BE_ARRAY!>T : <!FINAL_UPPER_BOUND!>IntArray<!><!>> f4() {}
|
fun <<!UPPER_BOUND_CANNOT_BE_ARRAY!>T : <!FINAL_UPPER_BOUND!>IntArray<!><!>> f4() {}
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ fun <<!UPPER_BOUND_CANNOT_BE_ARRAY!>T<!>> f5() where T : Array<Any> {}
|
|||||||
val <<!UPPER_BOUND_CANNOT_BE_ARRAY!>T : Array<Any?><!>> T.v: String get() = ""
|
val <<!UPPER_BOUND_CANNOT_BE_ARRAY!>T : Array<Any?><!>> T.v: String get() = ""
|
||||||
|
|
||||||
class C2<T, <!UPPER_BOUND_CANNOT_BE_ARRAY!>A : Array<out T><!>>
|
class C2<T, <!UPPER_BOUND_CANNOT_BE_ARRAY!>A : Array<out T><!>>
|
||||||
interface C3<S, T : S, <!UPPER_BOUND_CANNOT_BE_ARRAY!>A<!>> where A : Array<out S>, A : Array<out T>
|
interface C3<S, T : S, <!INCONSISTENT_TYPE_PARAMETER_BOUNDS, UPPER_BOUND_CANNOT_BE_ARRAY!>A<!>> where A : Array<out S>, A : Array<out T>
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
class C1<<!UPPER_BOUND_CANNOT_BE_ARRAY!>A : Array<Any><!>> {
|
class C1<<!UPPER_BOUND_CANNOT_BE_ARRAY!>A : Array<Any><!>> {
|
||||||
|
|||||||
@@ -6582,6 +6582,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("compiler/testData/diagnostics/tests/generics/nullability")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// See KT-9438: Enforce the Single Instantiation Inheritance Rule for type parameters
|
||||||
|
|
||||||
|
interface A
|
||||||
|
|
||||||
|
interface B
|
||||||
|
|
||||||
|
interface D<T>
|
||||||
|
|
||||||
|
interface CorrectF<<error descr="[INCONSISTENT_TYPE_PARAMETER_BOUNDS] Type parameter T of 'D' has inconsistent bounds: A, B">T</error>> where T : D<A>, T : D<B>
|
||||||
|
|
||||||
|
fun <<error descr="[INCONSISTENT_TYPE_PARAMETER_BOUNDS] Type parameter T of 'D' has inconsistent bounds: A, B">T</error>> bar() where T : D<A>, T : D<B> {}
|
||||||
@@ -325,6 +325,12 @@ public class JetPsiCheckerTestGenerated extends AbstractJetPsiCheckerTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("TypeParameterBounds.kt")
|
||||||
|
public void testTypeParameterBounds() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/checker/TypeParameterBounds.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("UnreachableCode.kt")
|
@TestMetadata("UnreachableCode.kt")
|
||||||
public void testUnreachableCode() throws Exception {
|
public void testUnreachableCode() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/checker/UnreachableCode.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/checker/UnreachableCode.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user