From 722a152a74c29cc1546183d848cfed55b520840b Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 23 Dec 2015 12:59:42 +0300 Subject: [PATCH] Fix supertypes calculation for types with projections Use captured types as replacement for non top-level entries #KT-7296 Fixed --- .../approximateDispatchReceiver.kt | 14 +++++++++++ .../approximateDispatchReceiver.txt | 19 +++++++++++++++ .../flexibleProjectedScope.kt | 16 +++++++++++++ .../flexibleProjectedScope.txt | 12 ++++++++++ .../recursiveUpperBoundStar.kt | 8 +++++-- .../recursiveUpperBoundStar.txt | 4 +++- .../recursiveUpperBoundStarOut.kt | 12 ++++++++++ .../recursiveUpperBoundStarOut.txt | 17 ++++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 18 +++++++++++++++ .../kotlin/types/TypeSubstitution.kt | 3 +++ .../jetbrains/kotlin/types/checker/utils.kt | 23 +++++++++++++++---- 11 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/approximateDispatchReceiver.kt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/approximateDispatchReceiver.txt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/flexibleProjectedScope.kt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/flexibleProjectedScope.txt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStarOut.kt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStarOut.txt diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/approximateDispatchReceiver.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/approximateDispatchReceiver.kt new file mode 100644 index 00000000000..3f8d756af45 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/approximateDispatchReceiver.kt @@ -0,0 +1,14 @@ +// !CHECK_TYPE + +public abstract class A { + fun bar(): String = "" +} + +public class B : A>() + +fun test(b: B<*>) { + // Here `bar` could have dispatch receiver parameter type 'A>', but it wouldn't work as + // since 'b' has type 'A>', so we should approximate dispatch receiver PARAMETER type to make it accept original receiver + b.bar() + b.bar() checkType { _() } +} diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/approximateDispatchReceiver.txt b/compiler/testData/diagnostics/tests/generics/projectionsScope/approximateDispatchReceiver.txt new file mode 100644 index 00000000000..ba6cfb050c5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/approximateDispatchReceiver.txt @@ -0,0 +1,19 @@ +package + +public fun test(/*0*/ b: B<*>): kotlin.Unit + +public abstract class A { + public constructor A() + public final fun bar(): kotlin.String + 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 final class B : A> { + public constructor B() + public final override /*1*/ /*fake_override*/ fun bar(): kotlin.String + 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/generics/projectionsScope/flexibleProjectedScope.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/flexibleProjectedScope.kt new file mode 100644 index 00000000000..663d2b3f00f --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/flexibleProjectedScope.kt @@ -0,0 +1,16 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// !CHECK_TYPE + +// FILE: Clazz.java +public class Clazz { + public java.util.Collection foo() { return null; } +} + +// FILE: main.kt + +public fun > Iterable.filterTo(destination: C, predicate: (T) -> Boolean) {} + +fun test(clazz: Clazz) { + val result = java.util.ArrayList() + clazz.foo().filterTo(result) { x -> true } +} diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/flexibleProjectedScope.txt b/compiler/testData/diagnostics/tests/generics/projectionsScope/flexibleProjectedScope.txt new file mode 100644 index 00000000000..4f8cd0921cc --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/flexibleProjectedScope.txt @@ -0,0 +1,12 @@ +package + +public fun test(/*0*/ clazz: Clazz): kotlin.Unit +public fun > kotlin.Iterable.filterTo(/*0*/ destination: C, /*1*/ predicate: (T) -> kotlin.Boolean): 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 foo(): kotlin.(Mutable)Collection! + 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/generics/projectionsScope/recursiveUpperBoundStar.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStar.kt index 2fbe845857a..b01b057768b 100644 --- a/compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStar.kt +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStar.kt @@ -4,7 +4,11 @@ interface A interface B : A> fun foo(x : B<*>) { - bar(x) // this should not be valid + bar1(x) // this should not be valid + bar2(x) + bar3(x) } -fun bar(x : A>) { } +fun bar1(x : A>) { } +fun bar2(x : A>) { } +fun bar3(x : A<*>) { } diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStar.txt b/compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStar.txt index cd0f39dae8b..972b1d58848 100644 --- a/compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStar.txt +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStar.txt @@ -1,6 +1,8 @@ package -public fun bar(/*0*/ x: A>): kotlin.Unit +public fun bar1(/*0*/ x: A>): kotlin.Unit +public fun bar2(/*0*/ x: A>): kotlin.Unit +public fun bar3(/*0*/ x: A<*>): kotlin.Unit public fun foo(/*0*/ x: B<*>): kotlin.Unit public interface A { diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStarOut.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStarOut.kt new file mode 100644 index 00000000000..80c221107c3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStarOut.kt @@ -0,0 +1,12 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// See KT-7296 +interface Out +interface B : Out> + +fun foo(x : B<*>) { + bar1(x) + bar2(x) +} + +fun bar1(x : Out>) { } +fun bar2(x : Out<*>) { } diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStarOut.txt b/compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStarOut.txt new file mode 100644 index 00000000000..233ecf210df --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStarOut.txt @@ -0,0 +1,17 @@ +package + +public fun bar1(/*0*/ x: Out>): kotlin.Unit +public fun bar2(/*0*/ x: Out<*>): kotlin.Unit +public fun foo(/*0*/ x: B<*>): kotlin.Unit + +public interface B : Out> { + 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 Out { + 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/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index db1c6f94060..b851d6725f4 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -7130,12 +7130,24 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/generics/projectionsScope"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("approximateDispatchReceiver.kt") + public void testApproximateDispatchReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/approximateDispatchReceiver.kt"); + doTest(fileName); + } + @TestMetadata("extensionResultSubstitution.kt") public void testExtensionResultSubstitution() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/extensionResultSubstitution.kt"); doTest(fileName); } + @TestMetadata("flexibleProjectedScope.kt") + public void testFlexibleProjectedScope() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/flexibleProjectedScope.kt"); + doTest(fileName); + } + @TestMetadata("kt7296.kt") public void testKt7296() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/kt7296.kt"); @@ -7172,6 +7184,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("recursiveUpperBoundStarOut.kt") + public void testRecursiveUpperBoundStarOut() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStarOut.kt"); + doTest(fileName); + } + @TestMetadata("starNullability.kt") public void testStarNullability() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/starNullability.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitution.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitution.kt index df7efd01394..3a475de8ae8 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitution.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitution.kt @@ -62,6 +62,9 @@ public abstract class TypeConstructorSubstitution : TypeSubstitution() { override fun isEmpty() = map.isEmpty() } + @JvmStatic + public fun create(kotlinType: KotlinType) = create(kotlinType.constructor, kotlinType.arguments) + @JvmStatic public fun create(typeConstructor: TypeConstructor, arguments: List): TypeSubstitution { val parameters = typeConstructor.parameters diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/utils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/utils.kt index a1259696fc2..b919cedc511 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/utils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/utils.kt @@ -16,11 +16,12 @@ package org.jetbrains.kotlin.types.checker +import org.jetbrains.kotlin.resolve.calls.inference.wrapWithCapturingSubstitution import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.TypeConstructorSubstitution import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.Variance -import org.jetbrains.kotlin.types.checker.TypeCheckingProcedureCallbacks +import org.jetbrains.kotlin.types.typesApproximation.approximateCapturedTypes import java.util.* private class SubtypePathNode(val type: KotlinType, val previous: SubtypePathNode?) @@ -46,8 +47,20 @@ public fun findCorrespondingSupertype( var currentPathNode = lastPathNode.previous while (currentPathNode != null) { - substituted = TypeSubstitutor.create(currentPathNode.type).safeSubstitute(substituted, Variance.INVARIANT) - isAnyMarkedNullable = isAnyMarkedNullable || currentPathNode.type.isMarkedNullable + val currentType = currentPathNode.type + if (currentType.arguments.any { it.projectionKind != Variance.INVARIANT }) { + substituted = TypeConstructorSubstitution.create(currentType) + .wrapWithCapturingSubstitution().buildSubstitutor() + .safeSubstitute(substituted, Variance.INVARIANT) + .approximate() + } + else { + substituted = TypeConstructorSubstitution.create(currentType) + .buildSubstitutor() + .safeSubstitute(substituted, Variance.INVARIANT) + } + + isAnyMarkedNullable = isAnyMarkedNullable || currentType.isMarkedNullable currentPathNode = currentPathNode.previous } @@ -62,3 +75,5 @@ public fun findCorrespondingSupertype( return null } + +private fun KotlinType.approximate() = approximateCapturedTypes(this).upper