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<T, Inv<T>> <: C<in String, R> we then were seeing the constraint T <= in String, and add the constaint T=Captured(in String) That lead to R=Inv<T>=Inv<Captured(in String)>, and after approximation R=Inv<in String>, that is not the desirable result (Inv<String> 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
This commit is contained in:
@@ -19,7 +19,7 @@ fun box(): String {
|
||||
val inv = o.lift(SwOperator())
|
||||
val signature = inv.javaClass.genericSuperclass.toString()
|
||||
|
||||
if (signature != "Inv<java.util.List<?>>") return "fail 1: $signature"
|
||||
if (signature != "Inv<java.util.List<? extends java.lang.CharSequence>>") return "fail 1: $signature"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// !CHECK_TYPE
|
||||
// SKIP_TXT
|
||||
fun test(b: S) {
|
||||
b.collect(toList()) checkType { _<Inv<String>>() }
|
||||
}
|
||||
|
||||
interface S {
|
||||
fun <R> collect(collector: C<in String, R>): R
|
||||
}
|
||||
|
||||
class C<X, Y>
|
||||
|
||||
fun <T> toList(): C<T, Inv<T>> = null!!
|
||||
|
||||
class Inv<Q>
|
||||
@@ -0,0 +1,11 @@
|
||||
// !CHECK_TYPE
|
||||
// SKIP_TXT
|
||||
import java.util.stream.Collectors
|
||||
import java.util.stream.Stream
|
||||
|
||||
fun test(a: Stream<String>) {
|
||||
a.collect(Collectors.toList()) checkType { _<MutableList<String>>() }
|
||||
// actually the inferred type is platform
|
||||
a.collect(Collectors.toList()) checkType { _<List<String?>>() }
|
||||
}
|
||||
|
||||
+6
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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<T> is a subtype, supertype or equal to Array<out Int> then T captures a type that extends Int: 'Captured(out Int)'
|
||||
// Array<T> is a subtype, supertype or equal to Array<in Int> then T captures a type that extends Int: 'Captured(in Int)'
|
||||
// Array<out Int> is a subtype or equal to Array<T> then T captures a type that extends Int: 'Captured(out Int)'
|
||||
// Array<in Int> is a subtype or equal to Array<T> 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user