Report error when type parameter has a type parameter bound and any other bound
To prevent issues with erasure on JVM: it's unclear what such type parameter should be erased to
This commit is contained in:
@@ -265,6 +265,7 @@ public interface Errors {
|
||||
DiagnosticFactory0<KtTypeReference> DYNAMIC_UPPER_BOUND = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtTypeReference> UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtTypeReference> ONLY_ONE_CLASS_BOUND_ALLOWED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtTypeParameter> BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtTypeReference> REPEATED_BOUND = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory1<KtNamedDeclaration, TypeParameterDescriptor> CONFLICTING_UPPER_BOUNDS =
|
||||
|
||||
+1
@@ -395,6 +395,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(FINAL_UPPER_BOUND, "''{0}'' is a final type, and thus a value of the type parameter is predetermined", RENDER_TYPE);
|
||||
MAP.put(UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE, "Extension function type can not be used as an upper bound");
|
||||
MAP.put(ONLY_ONE_CLASS_BOUND_ALLOWED, "Only one of the upper bounds can be a class");
|
||||
MAP.put(BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER, "Type parameter cannot have any other bounds if it's bounded by another type parameter");
|
||||
MAP.put(REPEATED_BOUND, "Type parameter already has this bound");
|
||||
MAP.put(DYNAMIC_UPPER_BOUND, "Dynamic type can not be used as an upper bound");
|
||||
MAP.put(USELESS_ELVIS, "Elvis operator (?:) always returns the left operand of non-nullable type {0}", RENDER_TYPE);
|
||||
|
||||
@@ -179,6 +179,17 @@ class DeclarationsChecker(
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkOnlyOneTypeParameterBound(descriptor: TypeParameterDescriptor, declaration: KtTypeParameter) {
|
||||
val upperBounds = descriptor.upperBounds
|
||||
val (boundsWhichAreTypeParameters, otherBounds) = upperBounds
|
||||
.map { type -> type.constructor }
|
||||
.partition { constructor -> constructor.declarationDescriptor is TypeParameterDescriptor }
|
||||
.let { pair -> pair.first.toSet() to pair.second.toSet() }
|
||||
if (boundsWhichAreTypeParameters.size > 1 || (boundsWhichAreTypeParameters.isNotEmpty() && otherBounds.isNotEmpty())) {
|
||||
trace.report(BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER.on(declaration))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkSupertypesForConsistency(classifier: ClassifierDescriptor, sourceElement: PsiElement) {
|
||||
if (classifier is TypeParameterDescriptor) {
|
||||
val immediateUpperBounds = classifier.upperBounds.map { it.constructor }
|
||||
@@ -330,13 +341,15 @@ class DeclarationsChecker(
|
||||
|
||||
private fun checkTypeParameterConstraints(typeParameterListOwner: KtTypeParameterListOwner) {
|
||||
val constraints = typeParameterListOwner.typeConstraints
|
||||
if (!constraints.isEmpty()) {
|
||||
for (typeParameter in typeParameterListOwner.typeParameters) {
|
||||
if (typeParameter.extendsBound != null && hasConstraints(typeParameter, constraints)) {
|
||||
trace.report(MISPLACED_TYPE_PARAMETER_CONSTRAINTS.on(typeParameter))
|
||||
}
|
||||
trace.get(TYPE_PARAMETER, typeParameter)?.let { checkSupertypesForConsistency(it, typeParameter) }
|
||||
if (constraints.isEmpty()) return
|
||||
|
||||
for (typeParameter in typeParameterListOwner.typeParameters) {
|
||||
if (typeParameter.extendsBound != null && hasConstraints(typeParameter, constraints)) {
|
||||
trace.report(MISPLACED_TYPE_PARAMETER_CONSTRAINTS.on(typeParameter))
|
||||
}
|
||||
val typeParameterDescriptor = trace.get(TYPE_PARAMETER, typeParameter) ?: continue
|
||||
checkSupertypesForConsistency(typeParameterDescriptor, typeParameter)
|
||||
checkOnlyOneTypeParameterBound(typeParameterDescriptor, typeParameter)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
fun example(value : Int) {
|
||||
val result: Int = if (value == 0) 1
|
||||
else if (value == 1) 2
|
||||
else throw IllegalArgumentException()
|
||||
result
|
||||
}
|
||||
|
||||
fun <T, S, U : T> foo(u: U) where U : S {}
|
||||
|
||||
|
||||
fun main(args : Array<String>) {
|
||||
foo(null!!)
|
||||
}
|
||||
|
||||
|
||||
fun box() = "OK"
|
||||
@@ -16,12 +16,6 @@ interface IncorrectH<<!MISPLACED_TYPE_PARAMETER_CONSTRAINTS!>T : G<D<A>><!>> whe
|
||||
|
||||
interface CorrectH<T> where T : G<D<A>>, T : <!REPEATED_BOUND!>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!>T: G<D<T>><!>> where T : <!REPEATED_BOUND!>G<D<T?>><!>
|
||||
|
||||
interface correctJ<T> where T : G<D<T>>, T : <!REPEATED_BOUND!>G<D<T?>><!>
|
||||
|
||||
@@ -38,14 +38,6 @@ public interface G</*0*/ T> {
|
||||
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
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
interface I1
|
||||
interface I2
|
||||
open class C
|
||||
|
||||
interface A1<K, V> where V : K, V : <!REPEATED_BOUND!>K<!>
|
||||
interface A2<K, V, <!BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER!>W<!>> where W : K, W : V
|
||||
interface A3<K, <!BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER!>V<!>> where V : I1, V : K, V : I2
|
||||
interface A4<<!BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER!>K<!>, V> where K : I1, K : I2, K : C, K : V, V : I2, V : I1
|
||||
|
||||
fun <K, V> f1() where V : K, V : <!REPEATED_BOUND!>K<!> {}
|
||||
fun <K, V, <!BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER!>W<!>> f2() where W : K, W : V {}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package
|
||||
|
||||
public fun </*0*/ K, /*1*/ V : K> f1(): kotlin.Unit where V : K
|
||||
public fun </*0*/ K, /*1*/ V, /*2*/ W : K> f2(): kotlin.Unit where W : V
|
||||
|
||||
public interface A1</*0*/ K, /*1*/ V : K> where V : K {
|
||||
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 A2</*0*/ K, /*1*/ V, /*2*/ W : K> where W : V {
|
||||
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 A3</*0*/ K, /*1*/ V : I1> where V : K, V : 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
|
||||
}
|
||||
|
||||
public interface A4</*0*/ K : I1, /*1*/ V : I2> where K : I2, K : C, K : V, V : 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 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
|
||||
}
|
||||
+1
-1
@@ -34,7 +34,7 @@ fun test4() {
|
||||
|
||||
//--------------
|
||||
|
||||
fun <T: A, R> emptyStrangeMap2(t: T): Map<T, R> where R: T, R: A = throw Exception("$t")
|
||||
fun <T: A, R> emptyStrangeMap2(t: T): Map<T, R> where R: T = throw Exception("$t")
|
||||
|
||||
fun test5(a: A) {
|
||||
emptyStrangeMap2(a)
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ package a {
|
||||
public fun </*0*/ T : a.A> emptyListOfA(): kotlin.List<T>
|
||||
public fun </*0*/ T : a.A, /*1*/ R : T> emptyStrangeMap(): kotlin.Map<T, R>
|
||||
public fun </*0*/ T, /*1*/ R : T> emptyStrangeMap1(/*0*/ t: T): kotlin.Map<T, R>
|
||||
public fun </*0*/ T : a.A, /*1*/ R : T> emptyStrangeMap2(/*0*/ t: T): kotlin.Map<T, R> where R : a.A
|
||||
public fun </*0*/ T : a.A, /*1*/ R : T> emptyStrangeMap2(/*0*/ t: T): kotlin.Map<T, R>
|
||||
public fun </*0*/ T : a.A, /*1*/ R : T> emptyStrangeMap3(/*0*/ r: R): kotlin.Map<T, R>
|
||||
public fun </*0*/ T, /*1*/ R : T> emptyStrangeMap4(/*0*/ l: kotlin.MutableList<T>): kotlin.Map<T, R>
|
||||
public fun </*0*/ U, /*1*/ V : U> foo(): U
|
||||
|
||||
-5
@@ -3,11 +3,6 @@ fun <NN: Any, NNN: NN> nonMisleadingNullable(
|
||||
<!UNUSED_PARAMETER!>nnn<!>: NNN?
|
||||
) {}
|
||||
|
||||
fun <NN: Any, TWO_BOUNDS> twoBounds(
|
||||
<!UNUSED_PARAMETER!>tb<!>: TWO_BOUNDS?
|
||||
|
||||
) where TWO_BOUNDS: Any, TWO_BOUNDS : NN {}
|
||||
|
||||
fun <T, N: T, INDIRECT: N> misleadingNullableSimple(
|
||||
<!UNUSED_PARAMETER!>t<!>: T?,
|
||||
<!UNUSED_PARAMETER!>t2<!>: T?,
|
||||
|
||||
-1
@@ -3,4 +3,3 @@ package
|
||||
public fun </*0*/ T> interactionWithRedundant(/*0*/ t: T?): kotlin.Unit
|
||||
public fun </*0*/ T, /*1*/ N : T, /*2*/ INDIRECT : N> misleadingNullableSimple(/*0*/ t: T?, /*1*/ t2: T?, /*2*/ n: N?, /*3*/ ind: INDIRECT?): kotlin.Unit
|
||||
public fun </*0*/ NN : kotlin.Any, /*1*/ NNN : NN> nonMisleadingNullable(/*0*/ nn: NN?, /*1*/ nnn: NNN?): kotlin.Unit
|
||||
public fun </*0*/ NN : kotlin.Any, /*1*/ TWO_BOUNDS : kotlin.Any> twoBounds(/*0*/ tb: TWO_BOUNDS?): kotlin.Unit where TWO_BOUNDS : NN
|
||||
|
||||
@@ -6708,6 +6708,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("TypeParametersInTypeParameterBounds.kt")
|
||||
public void testTypeParametersInTypeParameterBounds() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/generics/cyclicBounds")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
-6
@@ -6883,12 +6883,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/regressions"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("ea49318.kt")
|
||||
public void testEa49318() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/ea49318.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt3107.kt")
|
||||
public void testKt3107() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/kt3107.kt");
|
||||
|
||||
@@ -2,7 +2,7 @@ package test
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
class TypeParams<in T1 : Any, out T2, T3 : (Int) -> Int, T4, T5 : Any?, T6 : T5, T7 : Any> where T1 : Cloneable?, T1 : Serializable, T2 : String, T7 : T6 {
|
||||
class TypeParams<in T1 : Any, out T2, T3 : (Int) -> Int, T4, T5 : Any?, T6 : T5, T7> where T1 : Cloneable?, T1 : Serializable, T2 : String, T7 : T6 {
|
||||
|
||||
fun useParams(p1: T1, p2: (T2) -> Unit, p3: T3, p4: T4, P5: T5) {
|
||||
}
|
||||
@@ -17,7 +17,7 @@ class TypeParams<in T1 : Any, out T2, T3 : (Int) -> Int, T4, T5 : Any?, T6 : T5,
|
||||
fun <G1, G2, G3> withOwnParams(p1: G1, p2: G2, p3: G3, p4: T1, p5: (T2) -> Unit) {
|
||||
}
|
||||
|
||||
fun <G1 : Any?, G2 : G1, G3> withOwnParamsAndTypeConstraints(p1: G1, p2: G2, p3: G3, p4: T1, p5: (T2) -> Unit) where G3 : G1, G3 : String, G3 : Serializable? {
|
||||
fun <G1 : Any?, G2 : G1, G3, G4> withOwnParamsAndTypeConstraints(p1: G1, p2: G2, p3: G3, p4: T1, p5: (T2) -> Unit) where G4 : G1, G3 : String, G3 : Serializable? {
|
||||
}
|
||||
|
||||
fun <T1, T2, T3> withOwnParamsClashing(p1: T1, p2: T2, p3: T3, p4: T4, p5: T5) {
|
||||
|
||||
@@ -43,9 +43,7 @@ PsiJetFileStubImpl[package=test]
|
||||
TYPE_PARAMETER:[fqName=null, isInVariance=false, isOutVariance=false, name=T7]
|
||||
TYPE_REFERENCE:
|
||||
USER_TYPE:[isAbsoluteInRootPackage=false]
|
||||
USER_TYPE:[isAbsoluteInRootPackage=false]
|
||||
REFERENCE_EXPRESSION:[referencedName=kotlin]
|
||||
REFERENCE_EXPRESSION:[referencedName=Any]
|
||||
REFERENCE_EXPRESSION:[referencedName=T6]
|
||||
PRIMARY_CONSTRUCTOR:
|
||||
MODIFIER_LIST:[public]
|
||||
VALUE_PARAMETER_LIST:
|
||||
@@ -67,11 +65,6 @@ PsiJetFileStubImpl[package=test]
|
||||
REFERENCE_EXPRESSION:[referencedName=java]
|
||||
REFERENCE_EXPRESSION:[referencedName=io]
|
||||
REFERENCE_EXPRESSION:[referencedName=Serializable]
|
||||
TYPE_CONSTRAINT:
|
||||
REFERENCE_EXPRESSION:[referencedName=T7]
|
||||
TYPE_REFERENCE:
|
||||
USER_TYPE:[isAbsoluteInRootPackage=false]
|
||||
REFERENCE_EXPRESSION:[referencedName=T6]
|
||||
CLASS_BODY:
|
||||
PROPERTY:[fqName=test.TypeParams.useSomeParam, hasDelegate=false, hasDelegateExpression=false, hasInitializer=false, hasReturnTypeRef=true, isExtension=false, isTopLevel=false, isVar=false, name=useSomeParam]
|
||||
MODIFIER_LIST:[public final]
|
||||
@@ -313,6 +306,12 @@ PsiJetFileStubImpl[package=test]
|
||||
USER_TYPE:[isAbsoluteInRootPackage=false]
|
||||
REFERENCE_EXPRESSION:[referencedName=G1]
|
||||
TYPE_PARAMETER:[fqName=null, isInVariance=false, isOutVariance=false, name=G3]
|
||||
TYPE_REFERENCE:
|
||||
USER_TYPE:[isAbsoluteInRootPackage=false]
|
||||
USER_TYPE:[isAbsoluteInRootPackage=false]
|
||||
REFERENCE_EXPRESSION:[referencedName=kotlin]
|
||||
REFERENCE_EXPRESSION:[referencedName=String]
|
||||
TYPE_PARAMETER:[fqName=null, isInVariance=false, isOutVariance=false, name=G4]
|
||||
TYPE_REFERENCE:
|
||||
USER_TYPE:[isAbsoluteInRootPackage=false]
|
||||
REFERENCE_EXPRESSION:[referencedName=G1]
|
||||
@@ -352,13 +351,6 @@ PsiJetFileStubImpl[package=test]
|
||||
REFERENCE_EXPRESSION:[referencedName=kotlin]
|
||||
REFERENCE_EXPRESSION:[referencedName=Unit]
|
||||
TYPE_CONSTRAINT_LIST:
|
||||
TYPE_CONSTRAINT:
|
||||
REFERENCE_EXPRESSION:[referencedName=G3]
|
||||
TYPE_REFERENCE:
|
||||
USER_TYPE:[isAbsoluteInRootPackage=false]
|
||||
USER_TYPE:[isAbsoluteInRootPackage=false]
|
||||
REFERENCE_EXPRESSION:[referencedName=kotlin]
|
||||
REFERENCE_EXPRESSION:[referencedName=String]
|
||||
TYPE_CONSTRAINT:
|
||||
REFERENCE_EXPRESSION:[referencedName=G3]
|
||||
TYPE_REFERENCE:
|
||||
|
||||
+2
-2
@@ -2,6 +2,6 @@ package foo
|
||||
|
||||
import bar.*
|
||||
|
||||
/*p:foo*/fun <T : /*p:foo*/A?, B : /*p:foo*/Iterable</*p:foo*/Number>, C> test()
|
||||
where C : /*p:foo*/Number, C : /*p:foo*/Comparable</*p:foo*/Number>, C : B
|
||||
/*p:foo*/fun <T : /*p:foo*/A?, B : /*p:foo*/Iterable</*p:foo*/Number>, C, D> test()
|
||||
where C : /*p:foo*/Number, C : /*p:foo*/Comparable</*p:foo*/Number>, D : B
|
||||
{}
|
||||
|
||||
Reference in New Issue
Block a user