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 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<TypeProjection> constrainingArguments = constrainingType.getArguments();
|
||||
List<TypeParameterDescriptor> 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): <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
|
||||
@Override
|
||||
public Set<TypeParameterDescriptor> getTypeVariables() {
|
||||
|
||||
+1
-17
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@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");
|
||||
|
||||
Reference in New Issue
Block a user