diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/platformSuperClass.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/platformSuperClass.kt new file mode 100644 index 00000000000..9762496f347 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/platformSuperClass.kt @@ -0,0 +1,22 @@ +// !CHECK_TYPE + +// FILE: Clazz.java +public class Clazz { + public T getT() { return null; } + public Clazz getSuperClass() { return null; } +} + +// FILE: main.kt +fun test(clazz: Clazz<*>) { + clazz.t checkType { _() } + clazz.getSuperClass() checkType { _?>() } + clazz.getSuperClass().t checkType { _() } + + clazz.superClass checkType { _?>() } + clazz.superClass.t checkType { _() } + + // See KT-9294 + if (clazz.superClass == null) { + throw NullPointerException() + } +} diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/platformSuperClass.txt b/compiler/testData/diagnostics/tests/generics/projectionsScope/platformSuperClass.txt new file mode 100644 index 00000000000..cae93e4614f --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/platformSuperClass.txt @@ -0,0 +1,12 @@ +package + +public fun test(/*0*/ clazz: Clazz<*>): kotlin.Unit + +public open class Clazz { + public constructor Clazz() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun getSuperClass(): Clazz! + public open fun getT(): T! + 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/capturedTypes/captureForPlatformTypes.kt b/compiler/testData/diagnostics/tests/inference/capturedTypes/captureForPlatformTypes.kt index bf9bdc4a1bd..6509c48e8f9 100644 --- a/compiler/testData/diagnostics/tests/inference/capturedTypes/captureForPlatformTypes.kt +++ b/compiler/testData/diagnostics/tests/inference/capturedTypes/captureForPlatformTypes.kt @@ -16,14 +16,12 @@ fun test1(clazz: Class) { val foo0: Class = A.foo(clazz) val foo1 = A.foo(clazz) foo1 checkType { _< Class >() } -// should be ok - foo1 checkType { _< Class >() } + foo1 checkType { _< Class >() } } fun tes2t(clazz: Class) { val foo0: Class> = A.bar(clazz) val foo1 = A.bar(clazz) foo1 checkType { _< Class> >() } -// should be ok - foo1 checkType { _< Class> >() } + foo1 checkType { _< Class> >() } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/j+k/arrayOfStarParametrized.txt b/compiler/testData/diagnostics/tests/j+k/arrayOfStarParametrized.txt index 65ad05bf908..724e043fd4f 100644 --- a/compiler/testData/diagnostics/tests/j+k/arrayOfStarParametrized.txt +++ b/compiler/testData/diagnostics/tests/j+k/arrayOfStarParametrized.txt @@ -1,6 +1,6 @@ package -public fun foo1(/*0*/ x: A<*>): kotlin.Array> +public fun foo1(/*0*/ x: A<*>): kotlin.Array!>! public fun foo2(/*0*/ x: A<*>): kotlin.Unit public open class A { diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 4c48eb0b875..db1c6f94060 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -7160,6 +7160,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("platformSuperClass.kt") + public void testPlatformSuperClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/platformSuperClass.kt"); + doTest(fileName); + } + @TestMetadata("recursiveUpperBoundStar.kt") public void testRecursiveUpperBoundStar() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStar.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/CapturedTypeApproximation.kt b/core/descriptors/src/org/jetbrains/kotlin/types/CapturedTypeApproximation.kt index 8e75ca5d6d4..148f72cbdfd 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/CapturedTypeApproximation.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/CapturedTypeApproximation.kt @@ -53,7 +53,7 @@ private fun TypeArgument.toTypeProjection(): TypeProjection { } private fun TypeProjection.toTypeArgument(typeParameter: TypeParameterDescriptor) = - when (TypeSubstitutor.combine(typeParameter.variance, projectionKind)) { + when (TypeSubstitutor.combine(typeParameter.variance, this)) { Variance.INVARIANT -> TypeArgument(typeParameter, type, type) Variance.IN_VARIANCE -> TypeArgument(typeParameter, type, typeParameter.builtIns.nullableAnyType) Variance.OUT_VARIANCE -> TypeArgument(typeParameter, typeParameter.builtIns.nothingType, type) @@ -93,10 +93,21 @@ private fun substituteCapturedTypesWithProjections(typeProjection: TypeProjectio } public fun approximateCapturedTypes(type: KotlinType): ApproximationBounds { + if (type.isFlexible()) { + val boundsForFlexibleLower = approximateCapturedTypes(type.lowerIfFlexible()) + val boundsForFlexibleUpper = approximateCapturedTypes(type.upperIfFlexible()) + + val extraCapabilities = type.flexibility().extraCapabilities + return ApproximationBounds( + DelegatingFlexibleType.create( + boundsForFlexibleLower.lower.lowerIfFlexible(), boundsForFlexibleUpper.lower.upperIfFlexible(), extraCapabilities), + DelegatingFlexibleType.create( + boundsForFlexibleLower.upper.lowerIfFlexible(), boundsForFlexibleUpper.upper.upperIfFlexible(), extraCapabilities)) + } + val typeConstructor = type.constructor if (type.isCaptured()) { val typeProjection = (typeConstructor as CapturedTypeConstructor).typeProjection - // todo: preserve flexibility as well fun KotlinType.makeNullableIfNeeded() = TypeUtils.makeNullableIfNeeded(this, type.isMarkedNullable) val bound = typeProjection.type.makeNullableIfNeeded() @@ -112,9 +123,18 @@ public fun approximateCapturedTypes(type: KotlinType): ApproximationBounds() val upperBoundArguments = ArrayList() for ((typeProjection, typeParameter) in type.arguments.zip(typeConstructor.parameters)) { - val (lower, upper) = approximateProjection(typeProjection.toTypeArgument(typeParameter)) - lowerBoundArguments.add(lower) - upperBoundArguments.add(upper) + val typeArgument = typeProjection.toTypeArgument(typeParameter) + + // Protection from infinite recursion caused by star projection + if (typeProjection.isStarProjection) { + lowerBoundArguments.add(typeArgument) + upperBoundArguments.add(typeArgument) + } + else { + val (lower, upper) = approximateProjection(typeArgument) + lowerBoundArguments.add(lower) + upperBoundArguments.add(upper) + } } val lowerBoundIsTrivial = lowerBoundArguments.any { !it.isConsistent } return ApproximationBounds( diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitutor.java b/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitutor.java index ba94ab40418..6f601e32466 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitutor.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitutor.java @@ -260,6 +260,13 @@ public class TypeSubstitutor { return substitutedArguments; } + @NotNull + public static Variance combine(@NotNull Variance typeParameterVariance, @NotNull TypeProjection typeProjection) { + if (typeProjection.isStarProjection()) return Variance.OUT_VARIANCE; + + return combine(typeParameterVariance, typeProjection.getProjectionKind()); + } + @NotNull public static Variance combine(@NotNull Variance typeParameterVariance, @NotNull Variance projectionKind) { if (typeParameterVariance == Variance.INVARIANT) return projectionKind;