added variance handling to constraint system

This commit is contained in:
Svetlana Isakova
2012-08-03 17:23:00 +04:00
parent 04b2477784
commit 492d4da56d
5 changed files with 189 additions and 22 deletions
@@ -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() {
@@ -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");