From d3c17ec337fe35c9c747296bee651dc114a5a587 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 30 Nov 2015 22:50:20 +0300 Subject: [PATCH] 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 --- .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../kotlin/resolve/DeclarationsChecker.kt | 25 +++++++--- .../codegen/box/regressions/ea49318.kt | 16 ------- .../tests/generics/TypeParameterBounds.kt | 6 --- .../tests/generics/TypeParameterBounds.txt | 8 ---- .../TypeParametersInTypeParameterBounds.kt | 11 +++++ .../TypeParametersInTypeParameterBounds.txt | 47 +++++++++++++++++++ .../upperBounds/doNotInferFromBoundsOnly.kt | 2 +- .../upperBounds/doNotInferFromBoundsOnly.txt | 2 +- .../baseWithNullableUpperBound.kt | 5 -- .../baseWithNullableUpperBound.txt | 1 - .../checkers/DiagnosticsTestGenerated.java | 6 +++ .../BlackBoxCodegenTestGenerated.java | 6 --- .../stubBuilder/TypeParams/TypeParams.kt | 4 +- .../stubBuilder/TypeParams/TypeParams.txt | 22 +++------ .../classifierMembers/constraints.kt | 4 +- 17 files changed, 98 insertions(+), 69 deletions(-) delete mode 100644 compiler/testData/codegen/box/regressions/ea49318.kt create mode 100644 compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.kt create mode 100644 compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 746161e9243..07b1afb2451 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -265,6 +265,7 @@ public interface Errors { DiagnosticFactory0 DYNAMIC_UPPER_BOUND = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 ONLY_ONE_CLASS_BOUND_ALLOWED = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 REPEATED_BOUND = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 CONFLICTING_UPPER_BOUNDS = 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 ed8e6183c59..2f75f6aa1ae 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -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); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt index 2253c7d35a3..d22502c8edb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt @@ -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) } } diff --git a/compiler/testData/codegen/box/regressions/ea49318.kt b/compiler/testData/codegen/box/regressions/ea49318.kt deleted file mode 100644 index 863d8cf1f88..00000000000 --- a/compiler/testData/codegen/box/regressions/ea49318.kt +++ /dev/null @@ -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 foo(u: U) where U : S {} - - -fun main(args : Array) { - foo(null!!) -} - - -fun box() = "OK" \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.kt b/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.kt index 171a98eecde..6df85abf8c2 100644 --- a/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.kt +++ b/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.kt @@ -16,12 +16,6 @@ interface IncorrectH<T : G>> whe interface CorrectH 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 where T : G>, T : G> diff --git a/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.txt b/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.txt index 387b6f18db0..c9e35b9a2ec 100644 --- a/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.txt +++ b/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.txt @@ -38,14 +38,6 @@ public interface G { 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 diff --git a/compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.kt b/compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.kt new file mode 100644 index 00000000000..d823ffc3e61 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.kt @@ -0,0 +1,11 @@ +interface I1 +interface I2 +open class C + +interface A1 where V : K, V : K +interface A2W> where W : K, W : V +interface A3V> where V : I1, V : K, V : I2 +interface A4<K, V> where K : I1, K : I2, K : C, K : V, V : I2, V : I1 + +fun f1() where V : K, V : K {} +fun W> f2() where W : K, W : V {} diff --git a/compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.txt b/compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.txt new file mode 100644 index 00000000000..3d3fa582646 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.txt @@ -0,0 +1,47 @@ +package + +public fun f1(): kotlin.Unit where V : K +public fun f2(): kotlin.Unit where W : V + +public interface A1 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 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 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 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 +} diff --git a/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.kt b/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.kt index f49b4f20388..9d3eda60264 100644 --- a/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.kt +++ b/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.kt @@ -34,7 +34,7 @@ fun test4() { //-------------- -fun emptyStrangeMap2(t: T): Map where R: T, R: A = throw Exception("$t") +fun emptyStrangeMap2(t: T): Map where R: T = throw Exception("$t") fun test5(a: A) { emptyStrangeMap2(a) diff --git a/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.txt b/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.txt index daf4ee12b05..1140f63fead 100644 --- a/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.txt +++ b/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.txt @@ -5,7 +5,7 @@ package a { public fun emptyListOfA(): kotlin.List public fun emptyStrangeMap(): kotlin.Map public fun emptyStrangeMap1(/*0*/ t: T): kotlin.Map - public fun emptyStrangeMap2(/*0*/ t: T): kotlin.Map where R : a.A + public fun emptyStrangeMap2(/*0*/ t: T): kotlin.Map public fun emptyStrangeMap3(/*0*/ r: R): kotlin.Map public fun emptyStrangeMap4(/*0*/ l: kotlin.MutableList): kotlin.Map public fun foo(): U diff --git a/compiler/testData/diagnostics/tests/nullableTypes/baseWithNullableUpperBound.kt b/compiler/testData/diagnostics/tests/nullableTypes/baseWithNullableUpperBound.kt index 050d4824c42..16d11045916 100644 --- a/compiler/testData/diagnostics/tests/nullableTypes/baseWithNullableUpperBound.kt +++ b/compiler/testData/diagnostics/tests/nullableTypes/baseWithNullableUpperBound.kt @@ -3,11 +3,6 @@ fun nonMisleadingNullable( nnn: NNN? ) {} -fun twoBounds( - tb: TWO_BOUNDS? - -) where TWO_BOUNDS: Any, TWO_BOUNDS : NN {} - fun misleadingNullableSimple( t: T?, t2: T?, diff --git a/compiler/testData/diagnostics/tests/nullableTypes/baseWithNullableUpperBound.txt b/compiler/testData/diagnostics/tests/nullableTypes/baseWithNullableUpperBound.txt index a167c9ff69b..84c9eef3568 100644 --- a/compiler/testData/diagnostics/tests/nullableTypes/baseWithNullableUpperBound.txt +++ b/compiler/testData/diagnostics/tests/nullableTypes/baseWithNullableUpperBound.txt @@ -3,4 +3,3 @@ package public fun interactionWithRedundant(/*0*/ t: T?): kotlin.Unit public fun misleadingNullableSimple(/*0*/ t: T?, /*1*/ t2: T?, /*2*/ n: N?, /*3*/ ind: INDIRECT?): kotlin.Unit public fun nonMisleadingNullable(/*0*/ nn: NN?, /*1*/ nnn: NNN?): kotlin.Unit -public fun twoBounds(/*0*/ tb: TWO_BOUNDS?): kotlin.Unit where TWO_BOUNDS : NN diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index c7c2eca9978..42ebc220416 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -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) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java index 9fad80ad570..306736e8f50 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/idea/testData/decompiler/stubBuilder/TypeParams/TypeParams.kt b/idea/testData/decompiler/stubBuilder/TypeParams/TypeParams.kt index 2c61e98653d..d60977cee59 100644 --- a/idea/testData/decompiler/stubBuilder/TypeParams/TypeParams.kt +++ b/idea/testData/decompiler/stubBuilder/TypeParams/TypeParams.kt @@ -2,7 +2,7 @@ package test import java.io.Serializable -class TypeParams Int, T4, T5 : Any?, T6 : T5, T7 : Any> where T1 : Cloneable?, T1 : Serializable, T2 : String, T7 : T6 { +class TypeParams 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 Int, T4, T5 : Any?, T6 : T5, fun withOwnParams(p1: G1, p2: G2, p3: G3, p4: T1, p5: (T2) -> Unit) { } - fun withOwnParamsAndTypeConstraints(p1: G1, p2: G2, p3: G3, p4: T1, p5: (T2) -> Unit) where G3 : G1, G3 : String, G3 : Serializable? { + fun withOwnParamsAndTypeConstraints(p1: G1, p2: G2, p3: G3, p4: T1, p5: (T2) -> Unit) where G4 : G1, G3 : String, G3 : Serializable? { } fun withOwnParamsClashing(p1: T1, p2: T2, p3: T3, p4: T4, p5: T5) { diff --git a/idea/testData/decompiler/stubBuilder/TypeParams/TypeParams.txt b/idea/testData/decompiler/stubBuilder/TypeParams/TypeParams.txt index 2a12775533e..e1a04269bc7 100644 --- a/idea/testData/decompiler/stubBuilder/TypeParams/TypeParams.txt +++ b/idea/testData/decompiler/stubBuilder/TypeParams/TypeParams.txt @@ -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: diff --git a/jps-plugin/testData/incremental/lookupTracker/classifierMembers/constraints.kt b/jps-plugin/testData/incremental/lookupTracker/classifierMembers/constraints.kt index 1f41efa5406..579534024ae 100644 --- a/jps-plugin/testData/incremental/lookupTracker/classifierMembers/constraints.kt +++ b/jps-plugin/testData/incremental/lookupTracker/classifierMembers/constraints.kt @@ -2,6 +2,6 @@ package foo import bar.* -/*p:foo*/fun , C> test() - where C : /*p:foo*/Number, C : /*p:foo*/Comparable, C : B +/*p:foo*/fun , C, D> test() + where C : /*p:foo*/Number, C : /*p:foo*/Comparable, D : B {}