added variance handling to constraint system
This commit is contained in:
+96
-5
@@ -30,6 +30,9 @@ import org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraintsImpl.Constr
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraintsImpl.ConstraintKind.*;
|
||||||
|
import static org.jetbrains.jet.lang.types.Variance.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author svtk
|
* @author svtk
|
||||||
*/
|
*/
|
||||||
@@ -137,12 +140,12 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addSubtypingConstraint(@NotNull JetType subjectType, @Nullable JetType constrainingType, @NotNull ConstraintPosition constraintPosition) {
|
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
|
@Override
|
||||||
public void addSupertypeConstraint(@NotNull JetType subjectType, @Nullable JetType constrainingType, @NotNull ConstraintPosition constraintPosition) {
|
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,
|
private void addConstraint(@NotNull ConstraintKind constraintKind,
|
||||||
@@ -210,12 +213,100 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
|||||||
List<TypeProjection> constrainingArguments = constrainingType.getArguments();
|
List<TypeProjection> constrainingArguments = constrainingType.getArguments();
|
||||||
List<TypeParameterDescriptor> parameters = typeConstructor.getParameters();
|
List<TypeParameterDescriptor> parameters = typeConstructor.getParameters();
|
||||||
for (int i = 0; i < subjectArguments.size(); i++) {
|
for (int i = 0; i < subjectArguments.size(); i++) {
|
||||||
//todo constrainingArguments.get(i).getType() -> type projections
|
Variance typeParameterVariance = parameters.get(i).getVariance();
|
||||||
addConstraint(ConstraintKind.fromVariance(parameters.get(i).getVariance()), subjectArguments.get(i).getType(),
|
TypeProjection subjectArgument = subjectArguments.get(i);
|
||||||
constrainingArguments.get(i).getType(), constraintPosition);
|
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): <br/>
|
||||||
|
* {@code MyClass<in/out/- A> <: MyClass<in/out/- B>}, where {@code MyClass<in/out/- T>} is declared. <br/>
|
||||||
|
*
|
||||||
|
* 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<T> is declared.
|
||||||
|
//
|
||||||
|
// If super type has 'out' projection:
|
||||||
|
// MyClass<A> <: MyClass<out B>,
|
||||||
|
// then constraint A <: B can be generated.
|
||||||
|
//
|
||||||
|
// If super type has 'in' projection:
|
||||||
|
// MyClass<A> <: MyClass<in B>,
|
||||||
|
// 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<T, out R, in S>} is declared.<br/><br/>
|
||||||
|
*
|
||||||
|
* If upperConstraintKind is SUB_TYPE:
|
||||||
|
* {@code MyClass<A, B, C> <: MyClass<D, E, F>}, <br/>
|
||||||
|
* then constraints {@code A = D, B <: E, C >: F} are generated. <br/><br/>
|
||||||
|
*
|
||||||
|
* If upperConstraintKind is SUPER_TYPE:
|
||||||
|
* {@code MyClass<A, B, C> >: MyClass<D, E, F>}, <br/>
|
||||||
|
* then constraints {@code A = D, B >: E, C <: F} are generated. <br/><br/>
|
||||||
|
*
|
||||||
|
* If upperConstraintKind is EQUAL:
|
||||||
|
* {@code MyClass<A, B, C> = MyClass<D, E, F>}, <br/>
|
||||||
|
* then equality constraints {@code A = D, B = E, C = F} are generated. <br/><br/>
|
||||||
|
*
|
||||||
|
* 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
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Set<TypeParameterDescriptor> getTypeVariables() {
|
public Set<TypeParameterDescriptor> getTypeVariables() {
|
||||||
|
|||||||
+1
-17
@@ -93,22 +93,6 @@ public class TypeConstraintsImpl implements TypeConstraints {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static enum ConstraintKind {
|
public static enum ConstraintKind {
|
||||||
SUB_TYPE, SUPER_TYPE, EQUAL;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
class MyList<T>(t: T) {}
|
||||||
|
|
||||||
|
fun <T> getMyList(t: T) : MyList< T> = MyList(t)
|
||||||
|
fun <T> getMyListToWriteTo(t: T) : MyList< in T> = MyList(t)
|
||||||
|
fun <T> getMyListToReadFrom(t: T) : MyList<out T> = MyList(t)
|
||||||
|
|
||||||
|
fun <T> useMyList (<!UNUSED_PARAMETER!>l<!>: MyList< T>, <!UNUSED_PARAMETER!>t<!>: T) {}
|
||||||
|
fun <T> writeToMyList (<!UNUSED_PARAMETER!>l<!>: MyList< in T>, <!UNUSED_PARAMETER!>t<!>: T) {}
|
||||||
|
fun <T> readFromMyList(<!UNUSED_PARAMETER!>l<!>: MyList<out T>, <!UNUSED_PARAMETER!>t<!>: T) {}
|
||||||
|
|
||||||
|
fun test1(int: Int, any: Any) {
|
||||||
|
val a0 : MyList<Any> = getMyList(int)
|
||||||
|
|
||||||
|
val a1 : MyList<Int> = <!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>getMyList<!>(any)
|
||||||
|
|
||||||
|
val a2 : MyList<out Any> = getMyList(int)
|
||||||
|
|
||||||
|
val a3 : MyList<out Any> = getMyListToReadFrom(int)
|
||||||
|
|
||||||
|
val a4 : MyList<in Int> = getMyList(any)
|
||||||
|
|
||||||
|
val a5 : MyList<in Int> = getMyListToWriteTo(any)
|
||||||
|
|
||||||
|
|
||||||
|
val a6 : MyList<in Any> = <!TYPE_MISMATCH!>getMyList<Int>(int)<!>
|
||||||
|
val a7 : MyList<in Any> = getMyList(int)
|
||||||
|
|
||||||
|
val a8 : MyList<in Any> = <!TYPE_MISMATCH!>getMyListToReadFrom<Int>(int)<!>
|
||||||
|
val a9 : MyList<in Any> = <!TYPE_MISMATCH!>getMyListToReadFrom(int)<!>
|
||||||
|
|
||||||
|
val a10 : MyList<out Int> = <!TYPE_MISMATCH!>getMyList<Any>(any)<!>
|
||||||
|
val a11 : MyList<out Int> = <!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>getMyList<!>(any)
|
||||||
|
|
||||||
|
val a12 : MyList<out Int> = <!TYPE_MISMATCH!>getMyListToWriteTo<Any>(any)<!>
|
||||||
|
val a13 : MyList<out Int> = <!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>getMyListToWriteTo<!>(any)
|
||||||
|
|
||||||
|
useMyList(getMyList(int), int)
|
||||||
|
useMyList(getMyList(any), int)
|
||||||
|
<!TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS!>useMyList<!>(getMyList(int), any)
|
||||||
|
|
||||||
|
readFromMyList(getMyList(int), any)
|
||||||
|
readFromMyList(getMyList(any), int)
|
||||||
|
readFromMyList<Int>(<!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>getMyList<!>(any), int)
|
||||||
|
|
||||||
|
readFromMyList<Int>(<!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>getMyListToReadFrom<!>(any), int)
|
||||||
|
readFromMyList(getMyListToReadFrom(any), int)
|
||||||
|
|
||||||
|
readFromMyList(getMyListToReadFrom(int), any)
|
||||||
|
|
||||||
|
|
||||||
|
writeToMyList(getMyList(any), int)
|
||||||
|
writeToMyList<Any>(getMyList(int), any)
|
||||||
|
writeToMyList(getMyList<Any>(int), any)
|
||||||
|
<!TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS!>writeToMyList<!>(getMyList(int), any)
|
||||||
|
|
||||||
|
writeToMyList(getMyListToWriteTo(any), int)
|
||||||
|
<!TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS!>writeToMyList<!>(getMyListToWriteTo(int), any)
|
||||||
|
|
||||||
|
readFromMyList(<!TYPE_MISMATCH!>getMyListToWriteTo(any)<!>, any)
|
||||||
|
|
||||||
|
writeToMyList(<!TYPE_MISMATCH!>getMyListToReadFrom(any)<!>, any)
|
||||||
|
|
||||||
|
use(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun use(vararg a: Any) = a
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package b
|
||||||
|
//+JDK
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
import java.util.Collections.*
|
||||||
|
|
||||||
|
fun foo(list: List<String>) : String {
|
||||||
|
val w : String = max(list, comparator<String?> {o1, o2 -> 1
|
||||||
|
})!!
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
//from library
|
||||||
|
fun <T> comparator(<!UNUSED_PARAMETER!>fn<!>: (T,T) -> Int): Comparator<T> {<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
|
||||||
@@ -1269,6 +1269,16 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
|||||||
doTest("compiler/testData/diagnostics/tests/inference/dependOnExpectedType.kt");
|
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")
|
@TestMetadata("hasErrorInConstrainingTypes.kt")
|
||||||
public void testHasErrorInConstrainingTypes() throws Exception {
|
public void testHasErrorInConstrainingTypes() throws Exception {
|
||||||
doTest("compiler/testData/diagnostics/tests/inference/hasErrorInConstrainingTypes.kt");
|
doTest("compiler/testData/diagnostics/tests/inference/hasErrorInConstrainingTypes.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user