From 492d4da56d5aacbc7a8c9a7006151ec4b69ce725 Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Fri, 3 Aug 2012 17:23:00 +0400 Subject: [PATCH] added variance handling to constraint system --- .../calls/inference/ConstraintSystemImpl.java | 101 +++++++++++++++++- .../calls/inference/TypeConstraintsImpl.java | 18 +--- .../tests/inference/dependantOnVariance.kt | 68 ++++++++++++ .../inference/dependantOnVarianceNullable.kt | 14 +++ .../checkers/JetDiagnosticsTestGenerated.java | 10 ++ 5 files changed, 189 insertions(+), 22 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inference/dependantOnVariance.kt create mode 100644 compiler/testData/diagnostics/tests/inference/dependantOnVarianceNullable.kt diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java index 5df320ed699..4a55b396da9 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java @@ -30,6 +30,9 @@ import org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraintsImpl.Constr import java.util.*; +import static org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraintsImpl.ConstraintKind.*; +import static org.jetbrains.jet.lang.types.Variance.*; + /** * @author svtk */ @@ -137,12 +140,12 @@ public class ConstraintSystemImpl implements ConstraintSystem { @Override public void addSubtypingConstraint(@NotNull JetType subjectType, @Nullable JetType constrainingType, @NotNull ConstraintPosition constraintPosition) { - addConstraint(ConstraintKind.SUB_TYPE, subjectType, constrainingType, constraintPosition); + addConstraint(SUB_TYPE, subjectType, constrainingType, constraintPosition); } @Override public void addSupertypeConstraint(@NotNull JetType subjectType, @Nullable JetType constrainingType, @NotNull ConstraintPosition constraintPosition) { - addConstraint(ConstraintKind.SUPER_TYPE, subjectType, constrainingType, constraintPosition); + addConstraint(SUPER_TYPE, subjectType, constrainingType, constraintPosition); } private void addConstraint(@NotNull ConstraintKind constraintKind, @@ -210,12 +213,100 @@ public class ConstraintSystemImpl implements ConstraintSystem { List constrainingArguments = constrainingType.getArguments(); List parameters = typeConstructor.getParameters(); for (int i = 0; i < subjectArguments.size(); i++) { - //todo constrainingArguments.get(i).getType() -> type projections - addConstraint(ConstraintKind.fromVariance(parameters.get(i).getVariance()), subjectArguments.get(i).getType(), - constrainingArguments.get(i).getType(), constraintPosition); + Variance typeParameterVariance = parameters.get(i).getVariance(); + TypeProjection subjectArgument = subjectArguments.get(i); + TypeProjection constrainingArgument = constrainingArguments.get(i); + + ConstraintKind typeParameterConstraintKind = getTypeParameterConstraintKind(typeParameterVariance, + subjectArgument, constrainingArgument, constraintKind); + + addConstraint(typeParameterConstraintKind, subjectArgument.getType(), constrainingArgument.getType(), constraintPosition); } } + /** + * Determines what constraint (supertype, subtype or equal) should be generated for type parameter {@code T} in a constraint like (in this example subtype one):
+ * {@code MyClass <: MyClass}, where {@code MyClass} is declared.
+ * + * The parameters description is given according to the example above. + * @param typeParameterVariance declared variance of T + * @param subjectTypeProjection {@code in/out/- A} + * @param constrainingTypeProjection {@code in/out/- B} + * @param upperConstraintKind kind of the constraint {@code MyClass<...A> <: MyClass<...B>} (subtype in this example). + * @return kind of constraint to be generated: {@code A <: B} (subtype), {@code A >: B} (supertype) or {@code A = B} (equal). + */ + @NotNull + private static ConstraintKind getTypeParameterConstraintKind( + @NotNull Variance typeParameterVariance, + @NotNull TypeProjection subjectTypeProjection, + @NotNull TypeProjection constrainingTypeProjection, + @NotNull ConstraintKind upperConstraintKind + ) { + // If variance of type parameter is non-trivial, it should be taken into consideration to infer result constraint type. + // Otherwise when type parameter declared as INVARIANT, there might be non-trivial use-site variance of a supertype. + // + // Example: Let class MyClass is declared. + // + // If super type has 'out' projection: + // MyClass <: MyClass, + // then constraint A <: B can be generated. + // + // If super type has 'in' projection: + // MyClass <: MyClass, + // then constraint A >: B can be generated. + // + // Otherwise constraint A = B should be generated. + + Variance varianceForTypeParameter; + if (typeParameterVariance != INVARIANT) { + varianceForTypeParameter = typeParameterVariance; + } + else if (upperConstraintKind == SUB_TYPE) { + varianceForTypeParameter = constrainingTypeProjection.getProjectionKind(); + } + else if (upperConstraintKind == SUPER_TYPE) { + varianceForTypeParameter = subjectTypeProjection.getProjectionKind(); + } + else { + varianceForTypeParameter = INVARIANT; + } + + return getTypeParameterConstraintKind(varianceForTypeParameter, upperConstraintKind); + } + + /** + * Let class {@code MyClass} is declared.

+ * + * If upperConstraintKind is SUB_TYPE: + * {@code MyClass <: MyClass},
+ * then constraints {@code A = D, B <: E, C >: F} are generated.

+ * + * If upperConstraintKind is SUPER_TYPE: + * {@code MyClass >: MyClass},
+ * then constraints {@code A = D, B >: E, C <: F} are generated.

+ * + * If upperConstraintKind is EQUAL: + * {@code MyClass = MyClass},
+ * then equality constraints {@code A = D, B = E, C = F} are generated.

+ * + * Method getTypeParameterConstraintKind gets upperConstraintKind and variance of type parameter + * (INVARIANT for T, OUT_VARIANCE for R in example above) and returns kind of constraint for corresponding type parameter. + */ + @NotNull + private static ConstraintKind getTypeParameterConstraintKind( + @NotNull Variance typeParameterVariance, + @NotNull ConstraintKind upperConstraintKind + ) { + if (upperConstraintKind == EQUAL || typeParameterVariance == INVARIANT) { + return EQUAL; + } + if ((upperConstraintKind == SUPER_TYPE && typeParameterVariance == OUT_VARIANCE) || + (upperConstraintKind == SUB_TYPE && typeParameterVariance == IN_VARIANCE)) { + return SUPER_TYPE; + } + return SUB_TYPE; + } + @NotNull @Override public Set getTypeVariables() { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeConstraintsImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeConstraintsImpl.java index ac4aeda95b4..4b0ad02b8cf 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeConstraintsImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeConstraintsImpl.java @@ -93,22 +93,6 @@ public class TypeConstraintsImpl implements TypeConstraints { } public static enum ConstraintKind { - SUB_TYPE, SUPER_TYPE, EQUAL; - - @NotNull - static ConstraintKind fromVariance(@NotNull Variance variance) { - ConstraintKind constraintKind = null; - switch (variance) { - case INVARIANT: - constraintKind = EQUAL; - break; - case OUT_VARIANCE: - constraintKind = SUPER_TYPE; - break; - case IN_VARIANCE: - constraintKind = SUB_TYPE; - } - return constraintKind; - } + SUB_TYPE, SUPER_TYPE, EQUAL } } diff --git a/compiler/testData/diagnostics/tests/inference/dependantOnVariance.kt b/compiler/testData/diagnostics/tests/inference/dependantOnVariance.kt new file mode 100644 index 00000000000..21db1a85465 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/dependantOnVariance.kt @@ -0,0 +1,68 @@ +package a + +class MyList(t: T) {} + +fun getMyList(t: T) : MyList< T> = MyList(t) +fun getMyListToWriteTo(t: T) : MyList< in T> = MyList(t) +fun getMyListToReadFrom(t: T) : MyList = MyList(t) + +fun useMyList (l: MyList< T>, t: T) {} +fun writeToMyList (l: MyList< in T>, t: T) {} +fun readFromMyList(l: MyList, t: T) {} + +fun test1(int: Int, any: Any) { + val a0 : MyList = getMyList(int) + + val a1 : MyList = getMyList(any) + + val a2 : MyList = getMyList(int) + + val a3 : MyList = getMyListToReadFrom(int) + + val a4 : MyList = getMyList(any) + + val a5 : MyList = getMyListToWriteTo(any) + + + val a6 : MyList = getMyList(int) + val a7 : MyList = getMyList(int) + + val a8 : MyList = getMyListToReadFrom(int) + val a9 : MyList = getMyListToReadFrom(int) + + val a10 : MyList = getMyList(any) + val a11 : MyList = getMyList(any) + + val a12 : MyList = getMyListToWriteTo(any) + val a13 : MyList = getMyListToWriteTo(any) + + useMyList(getMyList(int), int) + useMyList(getMyList(any), int) + useMyList(getMyList(int), any) + + readFromMyList(getMyList(int), any) + readFromMyList(getMyList(any), int) + readFromMyList(getMyList(any), int) + + readFromMyList(getMyListToReadFrom(any), int) + readFromMyList(getMyListToReadFrom(any), int) + + readFromMyList(getMyListToReadFrom(int), any) + + + writeToMyList(getMyList(any), int) + writeToMyList(getMyList(int), any) + writeToMyList(getMyList(int), any) + writeToMyList(getMyList(int), any) + + writeToMyList(getMyListToWriteTo(any), int) + writeToMyList(getMyListToWriteTo(int), any) + + readFromMyList(getMyListToWriteTo(any), any) + + writeToMyList(getMyListToReadFrom(any), any) + + use(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) +} + +fun use(vararg a: Any) = a \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/dependantOnVarianceNullable.kt b/compiler/testData/diagnostics/tests/inference/dependantOnVarianceNullable.kt new file mode 100644 index 00000000000..cbdd402fc91 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/dependantOnVarianceNullable.kt @@ -0,0 +1,14 @@ +package b +//+JDK + +import java.util.* +import java.util.Collections.* + +fun foo(list: List) : String { + val w : String = max(list, comparator {o1, o2 -> 1 + })!! + return w +} + +//from library +fun comparator(fn: (T,T) -> Int): Comparator {} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 6f8442680ff..279fa0fd393 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -1269,6 +1269,16 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage doTest("compiler/testData/diagnostics/tests/inference/dependOnExpectedType.kt"); } + @TestMetadata("dependantOnVariance.kt") + public void testDependantOnVariance() throws Exception { + doTest("compiler/testData/diagnostics/tests/inference/dependantOnVariance.kt"); + } + + @TestMetadata("dependantOnVarianceNullable.kt") + public void testDependantOnVarianceNullable() throws Exception { + doTest("compiler/testData/diagnostics/tests/inference/dependantOnVarianceNullable.kt"); + } + @TestMetadata("hasErrorInConstrainingTypes.kt") public void testHasErrorInConstrainingTypes() throws Exception { doTest("compiler/testData/diagnostics/tests/inference/hasErrorInConstrainingTypes.kt");