A little more on common supertype inference. A big change is coming: the parameters must be inferred intelligently (including projection introductions). A few tests are failing: they are checking this non-existent behaviour

This commit is contained in:
Andrey Breslav
2011-01-27 22:36:12 +03:00
parent 4355bfe8e8
commit b70fcfb3f6
5 changed files with 136 additions and 30 deletions
@@ -24,11 +24,20 @@ public class ClassDescriptorResolver {
@Nullable
public ClassDescriptor resolveClassDescriptor(@NotNull JetScope scope, @NotNull JetClass classElement) {
TypeParameterExtensibleScope extensibleScope = new TypeParameterExtensibleScope(scope);
// This call has side-effects on the extensibleScope (fills it in)
List<TypeParameterDescriptor> typeParameters
= resolveTypeParameters(extensibleScope, classElement.getTypeParameters());
List<JetDelegationSpecifier> delegationSpecifiers = classElement.getDelegationSpecifiers();
Collection<? extends Type> superclasses = delegationSpecifiers.isEmpty()
? Collections.singleton(JetStandardClasses.getAnyType())
: resolveTypes(extensibleScope, delegationSpecifiers);
return new ClassDescriptor(
AttributeResolver.INSTANCE.resolveAttributes(classElement.getModifierList()),
classElement.getName(),
resolveTypeParameters(extensibleScope, classElement.getTypeParameters()),
resolveTypes(extensibleScope, classElement.getDelegationSpecifiers())
AttributeResolver.INSTANCE.resolveAttributes(classElement.getModifierList()),
classElement.getName(),
typeParameters,
superclasses
);
}
@@ -5,6 +5,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeTypes;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.JetScope;
import org.jetbrains.jet.lang.resolve.TypeResolver;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.*;
@@ -35,12 +38,12 @@ public class JetTypeChecker {
: "namespace" // for the root namespace
*/
public Type getType(JetExpression expression) {
public Type getType(@NotNull final JetScope scope, @NotNull JetExpression expression) {
final Type[] result = new Type[1];
expression.accept(new JetVisitor() {
@Override
public void visitParenthesizedExpression(JetParenthesizedExpression expression) {
result[0] = getType(expression.getExpression());
result[0] = getType(scope, expression.getExpression());
}
@Override
@@ -97,6 +100,22 @@ public class JetTypeChecker {
throw new UnsupportedOperationException("Return some reflection interface"); // TODO
}
@Override
public void visitBinaryWithTypeRHSExpression(JetBinaryExpressionWithTypeRHS expression) {
if (expression.getOperationSign() == JetTokens.COLON) {
Type actualType = getType(scope, expression.getLeft());
Type expectedType = TypeResolver.INSTANCE.resolveType(scope, expression.getRight());
if (isSubtypeOf(actualType, expectedType)) {
result[0] = expectedType;
return;
} else {
// TODO
throw new UnsupportedOperationException("Type mismatch: expected " + expectedType + " but found " + actualType);
}
}
throw new UnsupportedOperationException(); // TODO
}
@Override
public void visitIfExpression(JetIfExpression expression) {
// TODO : check condition type
@@ -106,8 +125,8 @@ public class JetTypeChecker {
// TODO : type-check the branch
result[0] = JetStandardClasses.getUnitType();
} else {
Type thenType = getType(expression.getThen());
Type elseType = getType(elseBranch);
Type thenType = getType(scope, expression.getThen());
Type elseType = getType(scope, elseBranch);
result[0] = commonSupertype(thenType, elseType);
}
}
@@ -117,7 +136,7 @@ public class JetTypeChecker {
List<JetExpression> entries = expression.getEntries();
List<Type> types = new ArrayList<Type>();
for (JetExpression entry : entries) {
types.add(getType(entry));
types.add(getType(scope, entry));
}
// TODO : labels
result[0] = JetStandardClasses.getTupleType(types);
@@ -149,7 +168,13 @@ public class JetTypeChecker {
assert !elseOrder.isEmpty() : "No common supertype";
// TODO: support multiple common supertypes
return elseOrder.get(0);
Type result = elseOrder.get(0);
if (thenType.isNullable() || elseType.isNullable()) {
return TypeUtils.makeNullable(result);
}
assert !result.isNullable();
return result;
}
private void topologicallySort(Type current, Map<TypeConstructor, Type> visited, List<Type> topologicalOrder, @Nullable Map<TypeConstructor, Type> filter) {
@@ -165,11 +190,11 @@ public class JetTypeChecker {
assert equalTypes(visited.get(supertypeConstructor), supertype);
continue;
}
Type substitutedSupertype = substitute(substitutionContext, supertype);
Type substitutedSupertype = substituteInType(substitutionContext, supertype);
topologicallySort(substitutedSupertype, visited, topologicalOrder, filter);
}
if (filter != null && filter.containsKey(current.getConstructor())) {
assert equalTypes(filter.get(current.getConstructor()), current);
assert equalTypes(filter.get(current.getConstructor()), current) : filter.get(current.getConstructor()) + " != " + current;
topologicalOrder.add(0, current);
}
}
@@ -204,7 +229,7 @@ public class JetTypeChecker {
for (Type immediateSupertype : constructor.getSupertypes()) {
Type correspondingSupertype = findCorrespondingSupertype(immediateSupertype, supertype);
if (correspondingSupertype != null) {
return substitute(getSubstitutionContext(subtype), correspondingSupertype);
return substituteInType(getSubstitutionContext(subtype), correspondingSupertype);
}
}
return null;
@@ -223,17 +248,34 @@ public class JetTypeChecker {
return parameterValues;
}
private Type substituteInType(Map<TypeConstructor, TypeProjection> substitutionContext, Type type) {
TypeProjection value = substitutionContext.get(type.getConstructor());
assert value == null : "For now this is used only for supertypes, thus no variables";
return specializeType(type, substituteInArguments(substitutionContext, type));
}
@NotNull
private Type substitute(Map<TypeConstructor, TypeProjection> parameterValues, Type subject) {
TypeProjection value = parameterValues.get(subject.getConstructor());
private TypeProjection substitute(Map<TypeConstructor, TypeProjection> parameterValues, TypeProjection subject) {
ProjectionKind projectionKind = subject.getProjectionKind();
if (projectionKind == ProjectionKind.NEITHER_OUT_NOR_IN) {
return subject;
}
@NotNull Type subjectType = subject.getType();
TypeProjection value = parameterValues.get(subjectType.getConstructor());
if (value != null) {
return value.getType();
return value;
}
List<TypeProjection> newArguments = substituteInArguments(parameterValues, subjectType);
return new TypeProjection(projectionKind, specializeType(subjectType, newArguments));
}
private List<TypeProjection> substituteInArguments(Map<TypeConstructor, TypeProjection> parameterValues, Type subjectType) {
List<TypeProjection> newArguments = new ArrayList<TypeProjection>();
for (TypeProjection argument : subject.getArguments()) {
newArguments.add(new TypeProjection(argument.getProjectionKind(), substitute(parameterValues, argument.getType())));
for (TypeProjection argument : subjectType.getArguments()) {
newArguments.add(substitute(parameterValues, argument));
}
return specializeType(subject, newArguments);
return newArguments;
}
private Type specializeType(Type type, List<TypeProjection> newArguments) {
@@ -315,7 +357,7 @@ public class JetTypeChecker {
return true;
}
public boolean equalTypes(Type type1, Type type2) {
public boolean equalTypes(@NotNull Type type1, @NotNull Type type2) {
if (!type1.getConstructor().equals(type2.getConstructor())) {
return false;
}
@@ -330,8 +372,10 @@ public class JetTypeChecker {
if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) {
return false;
}
if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) {
return false;
if (typeProjection1.getProjectionKind() != ProjectionKind.NEITHER_OUT_NOR_IN) {
if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) {
return false;
}
}
}
return true;
@@ -26,6 +26,12 @@ public class TypeProjection {
@Override
public String toString() {
if (projection == ProjectionKind.NEITHER_OUT_NOR_IN) {
return projection.toString();
}
if (projection == ProjectionKind.NO_PROJECTION) {
return type + "";
}
return projection + " " + type;
}
}
@@ -0,0 +1,13 @@
package org.jetbrains.jet.lang.types;
/**
* @author abreslav
*/
public class TypeUtils {
public static Type makeNullable(Type type) {
if (type.isNullable()) {
return type;
}
return new TypeImpl(type.getAttributes(), type.getConstructor(), true, type.getArguments(), type.getMemberDomain());
}
}
@@ -77,11 +77,27 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
}
public void testIf() throws Exception {
assertType("if (true) 1", JetStandardClasses.getUnitType());
assertType("if (true) 1 else 1", JetStandardClasses.getIntType());
assertType("if (true) 1 else return", JetStandardClasses.getIntType());
assertType("if (true) return else 1", JetStandardClasses.getIntType());
assertType("if (true) return else return", JetStandardClasses.getNothingType());
assertType("if (true) 1", "Unit");
assertType("if (true) 1 else 1", "Int");
assertType("if (true) 1 else return", "Int");
assertType("if (true) return else 1", "Int");
assertType("if (true) return else return", "Nothing");
assertType("if (true) 1 else null", "Int?");
assertType("if (true) null else null", "Nothing?");
assertType("if (true) 1 else '1'", "Any");
assertType("if (true) null : Base_T<*>? else null : Derived_T<*>?", "Base_T<*>?");
assertType("if (true) null : Base_inT<*>? else null : Derived_T<*>?", "Any?");
assertType("if (true) null : DDerived_T<Int>? else null : Derived_T<Int>?", "Derived_T<Int>?");
assertType("if (true) null : DDerived_T<Int>? else null : DDerived1_T<Int>?", "Derived_T<Int>?");
assertType("if (true) null : Base_T<Int>? else null : Base_T<Boolean>?", "Any?");
assertType("if (true) null : Base_T<Int>? else null : Base_T<in Int>?", "Base_T<in Int>?");
assertType("if (true) null : Derived_T<Int>? else null : Base_T<in Int>?", "Base_T<in Int>?");
assertType("if (true) null : Derived_T<in Int>? else null : Base_T<Int>?", "Any?");
assertType("if (true) null : Base_T<Int>? else null : Base_T<*>?", "Base_T<*>?");
}
public void testBasicSubtyping() throws Exception {
@@ -187,6 +203,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
assertSubtype("Derived_T<Int>", "Base_T<Int>");
assertSubtype("Derived_outT<Int>", "Base_outT<Int>");
assertSubtype("Derived_inT<Int>", "Base_inT<Int>");
assertSubtype("Derived_T<*>", "Base_T<*>");
assertNotSubtype("Derived_T<Int>", "Base_T<Any>");
@@ -212,6 +229,9 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
assertSubtype("Nothing", "Int");
assertSubtype("Nothing?", "Int?");
assertNotSubtype("Nothing?", "Int");
assertSubtype("Nothing?", "Base_T<*>?");
assertSubtype("Nothing?", "Derived_T<*>?");
}
public void testImplicitConversions() throws Exception {
@@ -227,8 +247,8 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
}
private static void assertSubtypingRelation(String type1, String type2, boolean expected) {
Type typeNode1 = TypeResolver.INSTANCE.resolveType(ClassDefinitions.BASIC_SCOPE, JetChangeUtil.createType(getProject(), type1));
Type typeNode2 = TypeResolver.INSTANCE.resolveType(ClassDefinitions.BASIC_SCOPE, JetChangeUtil.createType(getProject(), type2));
Type typeNode1 = makeType(type1);
Type typeNode2 = makeType(type2);
boolean result = JetTypeChecker.INSTANCE.isSubtypeOf(
typeNode1,
typeNode2);
@@ -253,15 +273,29 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
private static void assertType(String expression, Type expectedType) {
Project project = getProject();
JetExpression jetExpression = JetChangeUtil.createExpression(project, expression);
Type type = JetTypeChecker.INSTANCE.getType(jetExpression);
Type type = JetTypeChecker.INSTANCE.getType(ClassDefinitions.BASIC_SCOPE, jetExpression);
assertTrue(type + "!=" + expectedType, JetTypeChecker.INSTANCE.equalTypes(type, expectedType));
}
private static void assertType(String expression, String expectedTypeStr) {
Project project = getProject();
JetExpression jetExpression = JetChangeUtil.createExpression(project, expression);
Type type = JetTypeChecker.INSTANCE.getType(ClassDefinitions.BASIC_SCOPE, jetExpression);
Type expectedType = makeType(expectedTypeStr);
assertTrue(type + "!=" + expectedType, JetTypeChecker.INSTANCE.equalTypes(type, expectedType));
}
private static Type makeType(String typeStr) {
return TypeResolver.INSTANCE.resolveType(ClassDefinitions.BASIC_SCOPE, JetChangeUtil.createType(getProject(), typeStr));
}
private static class ClassDefinitions {
private static Map<String, ClassDescriptor> CLASSES = new HashMap<String, ClassDescriptor>();
private static String[] CLASS_DECLARATIONS = {
"class Base_T<T>",
"class Derived_T<T> : Base_T<T>",
"class DDerived_T<T> : Derived_T<T>",
"class DDerived1_T<T> : Derived_T<T>",
"class Base_inT<in T>",
"class Derived_inT<in T> : Base_inT<T>",
"class Base_outT<out T>",