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:
Alexander Udalov
2015-11-30 22:50:20 +03:00
parent b1fa740341
commit d3c17ec337
17 changed files with 98 additions and 69 deletions
@@ -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 =
@@ -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)
}
}
-16
View File
@@ -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
@@ -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 {}
@@ -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
}
@@ -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)
@@ -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
@@ -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?,
@@ -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)
@@ -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,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
{}