From 2edcd369a866e98e83b1a808a0dd41c63603f399 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 20 Jan 2017 15:32:05 +0300 Subject: [PATCH] Fix inference issue with Stream.collect See doNotCaptureSupertype test for clarification: When resolving b.collect(toList()) we're building a common system with two variables T and R. The problem was that when introducing the constraint C> <: C we then were seeing the constraint T <= in String, and add the constaint T=Captured(in String) That lead to R=Inv=Inv, and after approximation R=Inv, that is not the desirable result (Inv suits here) But the root problem was that we add captured constaint when projection was from supertype, that seems to be wrong, and for example Java doesn't do that in the similar situation. #KT-11259 Fixed --- .../box/reified/approximateCapturedTypes.kt | 2 +- .../tests/generics/doNotCaptureSupertype.kt | 15 +++++++++++++++ .../testsWithJava8/collectorInference.kt | 11 +++++++++++ .../DiagnosticsWithJava8TestGenerated.java | 6 ++++++ .../kotlin/checkers/DiagnosticsTestGenerated.java | 6 ++++++ .../types/checker/TypeCheckingProcedure.java | 15 ++++++--------- 6 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/generics/doNotCaptureSupertype.kt create mode 100644 compiler/testData/diagnostics/testsWithJava8/collectorInference.kt diff --git a/compiler/testData/codegen/box/reified/approximateCapturedTypes.kt b/compiler/testData/codegen/box/reified/approximateCapturedTypes.kt index 7febb78913a..3ea0849d898 100644 --- a/compiler/testData/codegen/box/reified/approximateCapturedTypes.kt +++ b/compiler/testData/codegen/box/reified/approximateCapturedTypes.kt @@ -19,7 +19,7 @@ fun box(): String { val inv = o.lift(SwOperator()) val signature = inv.javaClass.genericSuperclass.toString() - if (signature != "Inv>") return "fail 1: $signature" + if (signature != "Inv>") return "fail 1: $signature" return "OK" } diff --git a/compiler/testData/diagnostics/tests/generics/doNotCaptureSupertype.kt b/compiler/testData/diagnostics/tests/generics/doNotCaptureSupertype.kt new file mode 100644 index 00000000000..6f32d24fe2a --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/doNotCaptureSupertype.kt @@ -0,0 +1,15 @@ +// !CHECK_TYPE +// SKIP_TXT +fun test(b: S) { + b.collect(toList()) checkType { _>() } +} + +interface S { + fun collect(collector: C): R +} + +class C + +fun toList(): C> = null!! + +class Inv diff --git a/compiler/testData/diagnostics/testsWithJava8/collectorInference.kt b/compiler/testData/diagnostics/testsWithJava8/collectorInference.kt new file mode 100644 index 00000000000..7c393935c73 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava8/collectorInference.kt @@ -0,0 +1,11 @@ +// !CHECK_TYPE +// SKIP_TXT +import java.util.stream.Collectors +import java.util.stream.Stream + +fun test(a: Stream) { + a.collect(Collectors.toList()) checkType { _>() } + // actually the inferred type is platform + a.collect(Collectors.toList()) checkType { _>() } +} + diff --git a/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJava8TestGenerated.java b/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJava8TestGenerated.java index a2e0435d6f7..97909fbac75 100644 --- a/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJava8TestGenerated.java +++ b/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJava8TestGenerated.java @@ -36,6 +36,12 @@ public class DiagnosticsWithJava8TestGenerated extends AbstractDiagnosticsWithFu KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithJava8"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("collectorInference.kt") + public void testCollectorInference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJava8/collectorInference.kt"); + doTest(fileName); + } + @TestMetadata("defaultMethods.kt") public void testDefaultMethods() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJava8/defaultMethods.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index b8c949dde09..bdce5cb01b1 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -8506,6 +8506,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("doNotCaptureSupertype.kt") + public void testDoNotCaptureSupertype() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/doNotCaptureSupertype.kt"); + doTest(fileName); + } + @TestMetadata("genericsInType.kt") public void testGenericsInType() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/genericsInType.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/TypeCheckingProcedure.java b/core/descriptors/src/org/jetbrains/kotlin/types/checker/TypeCheckingProcedure.java index a4770179f9e..6e990812da2 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/TypeCheckingProcedure.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/TypeCheckingProcedure.java @@ -264,22 +264,19 @@ public class TypeCheckingProcedure { } private boolean capture( - @NotNull TypeProjection firstProjection, - @NotNull TypeProjection secondProjection, + @NotNull TypeProjection subtypeArgumentProjection, + @NotNull TypeProjection supertypeArgumentProjection, @NotNull TypeParameterDescriptor parameter ) { // Capturing makes sense only for invariant classes if (parameter.getVariance() != INVARIANT) return false; // Now, both subtype and supertype relations transform to equality constraints on type arguments: - // Array is a subtype, supertype or equal to Array then T captures a type that extends Int: 'Captured(out Int)' - // Array is a subtype, supertype or equal to Array then T captures a type that extends Int: 'Captured(in Int)' + // Array is a subtype or equal to Array then T captures a type that extends Int: 'Captured(out Int)' + // Array is a subtype or equal to Array then T captures a type that extends Int: 'Captured(in Int)' - if (firstProjection.getProjectionKind() == INVARIANT && secondProjection.getProjectionKind() != INVARIANT) { - return constraints.capture(firstProjection.getType(), secondProjection); - } - if (firstProjection.getProjectionKind() != INVARIANT && secondProjection.getProjectionKind() == INVARIANT) { - return constraints.capture(secondProjection.getType(), firstProjection); + if (subtypeArgumentProjection.getProjectionKind() != INVARIANT && supertypeArgumentProjection.getProjectionKind() == INVARIANT) { + return constraints.capture(supertypeArgumentProjection.getType(), subtypeArgumentProjection); } return false; }