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 @Nullable
public ClassDescriptor resolveClassDescriptor(@NotNull JetScope scope, @NotNull JetClass classElement) { public ClassDescriptor resolveClassDescriptor(@NotNull JetScope scope, @NotNull JetClass classElement) {
TypeParameterExtensibleScope extensibleScope = new TypeParameterExtensibleScope(scope); 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( return new ClassDescriptor(
AttributeResolver.INSTANCE.resolveAttributes(classElement.getModifierList()), AttributeResolver.INSTANCE.resolveAttributes(classElement.getModifierList()),
classElement.getName(), classElement.getName(),
resolveTypeParameters(extensibleScope, classElement.getTypeParameters()), typeParameters,
resolveTypes(extensibleScope, classElement.getDelegationSpecifiers()) superclasses
); );
} }
@@ -5,6 +5,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeTypes; import org.jetbrains.jet.JetNodeTypes;
import org.jetbrains.jet.lang.psi.*; 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.*; import java.util.*;
@@ -35,12 +38,12 @@ public class JetTypeChecker {
: "namespace" // for the root namespace : "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]; final Type[] result = new Type[1];
expression.accept(new JetVisitor() { expression.accept(new JetVisitor() {
@Override @Override
public void visitParenthesizedExpression(JetParenthesizedExpression expression) { public void visitParenthesizedExpression(JetParenthesizedExpression expression) {
result[0] = getType(expression.getExpression()); result[0] = getType(scope, expression.getExpression());
} }
@Override @Override
@@ -97,6 +100,22 @@ public class JetTypeChecker {
throw new UnsupportedOperationException("Return some reflection interface"); // TODO 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 @Override
public void visitIfExpression(JetIfExpression expression) { public void visitIfExpression(JetIfExpression expression) {
// TODO : check condition type // TODO : check condition type
@@ -106,8 +125,8 @@ public class JetTypeChecker {
// TODO : type-check the branch // TODO : type-check the branch
result[0] = JetStandardClasses.getUnitType(); result[0] = JetStandardClasses.getUnitType();
} else { } else {
Type thenType = getType(expression.getThen()); Type thenType = getType(scope, expression.getThen());
Type elseType = getType(elseBranch); Type elseType = getType(scope, elseBranch);
result[0] = commonSupertype(thenType, elseType); result[0] = commonSupertype(thenType, elseType);
} }
} }
@@ -117,7 +136,7 @@ public class JetTypeChecker {
List<JetExpression> entries = expression.getEntries(); List<JetExpression> entries = expression.getEntries();
List<Type> types = new ArrayList<Type>(); List<Type> types = new ArrayList<Type>();
for (JetExpression entry : entries) { for (JetExpression entry : entries) {
types.add(getType(entry)); types.add(getType(scope, entry));
} }
// TODO : labels // TODO : labels
result[0] = JetStandardClasses.getTupleType(types); result[0] = JetStandardClasses.getTupleType(types);
@@ -149,7 +168,13 @@ public class JetTypeChecker {
assert !elseOrder.isEmpty() : "No common supertype"; assert !elseOrder.isEmpty() : "No common supertype";
// TODO: support multiple common supertypes // 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) { 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); assert equalTypes(visited.get(supertypeConstructor), supertype);
continue; continue;
} }
Type substitutedSupertype = substitute(substitutionContext, supertype); Type substitutedSupertype = substituteInType(substitutionContext, supertype);
topologicallySort(substitutedSupertype, visited, topologicalOrder, filter); topologicallySort(substitutedSupertype, visited, topologicalOrder, filter);
} }
if (filter != null && filter.containsKey(current.getConstructor())) { 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); topologicalOrder.add(0, current);
} }
} }
@@ -204,7 +229,7 @@ public class JetTypeChecker {
for (Type immediateSupertype : constructor.getSupertypes()) { for (Type immediateSupertype : constructor.getSupertypes()) {
Type correspondingSupertype = findCorrespondingSupertype(immediateSupertype, supertype); Type correspondingSupertype = findCorrespondingSupertype(immediateSupertype, supertype);
if (correspondingSupertype != null) { if (correspondingSupertype != null) {
return substitute(getSubstitutionContext(subtype), correspondingSupertype); return substituteInType(getSubstitutionContext(subtype), correspondingSupertype);
} }
} }
return null; return null;
@@ -223,17 +248,34 @@ public class JetTypeChecker {
return parameterValues; 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 @NotNull
private Type substitute(Map<TypeConstructor, TypeProjection> parameterValues, Type subject) { private TypeProjection substitute(Map<TypeConstructor, TypeProjection> parameterValues, TypeProjection subject) {
TypeProjection value = parameterValues.get(subject.getConstructor()); 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) { 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>(); List<TypeProjection> newArguments = new ArrayList<TypeProjection>();
for (TypeProjection argument : subject.getArguments()) { for (TypeProjection argument : subjectType.getArguments()) {
newArguments.add(new TypeProjection(argument.getProjectionKind(), substitute(parameterValues, argument.getType()))); newArguments.add(substitute(parameterValues, argument));
} }
return specializeType(subject, newArguments); return newArguments;
} }
private Type specializeType(Type type, List<TypeProjection> newArguments) { private Type specializeType(Type type, List<TypeProjection> newArguments) {
@@ -315,7 +357,7 @@ public class JetTypeChecker {
return true; return true;
} }
public boolean equalTypes(Type type1, Type type2) { public boolean equalTypes(@NotNull Type type1, @NotNull Type type2) {
if (!type1.getConstructor().equals(type2.getConstructor())) { if (!type1.getConstructor().equals(type2.getConstructor())) {
return false; return false;
} }
@@ -330,8 +372,10 @@ public class JetTypeChecker {
if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) { if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) {
return false; return false;
} }
if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) { if (typeProjection1.getProjectionKind() != ProjectionKind.NEITHER_OUT_NOR_IN) {
return false; if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) {
return false;
}
} }
} }
return true; return true;
@@ -26,6 +26,12 @@ public class TypeProjection {
@Override @Override
public String toString() { public String toString() {
if (projection == ProjectionKind.NEITHER_OUT_NOR_IN) {
return projection.toString();
}
if (projection == ProjectionKind.NO_PROJECTION) {
return type + "";
}
return projection + " " + 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 { public void testIf() throws Exception {
assertType("if (true) 1", JetStandardClasses.getUnitType()); assertType("if (true) 1", "Unit");
assertType("if (true) 1 else 1", JetStandardClasses.getIntType()); assertType("if (true) 1 else 1", "Int");
assertType("if (true) 1 else return", JetStandardClasses.getIntType()); assertType("if (true) 1 else return", "Int");
assertType("if (true) return else 1", JetStandardClasses.getIntType()); assertType("if (true) return else 1", "Int");
assertType("if (true) return else return", JetStandardClasses.getNothingType()); 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 { public void testBasicSubtyping() throws Exception {
@@ -187,6 +203,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
assertSubtype("Derived_T<Int>", "Base_T<Int>"); assertSubtype("Derived_T<Int>", "Base_T<Int>");
assertSubtype("Derived_outT<Int>", "Base_outT<Int>"); assertSubtype("Derived_outT<Int>", "Base_outT<Int>");
assertSubtype("Derived_inT<Int>", "Base_inT<Int>"); assertSubtype("Derived_inT<Int>", "Base_inT<Int>");
assertSubtype("Derived_T<*>", "Base_T<*>");
assertNotSubtype("Derived_T<Int>", "Base_T<Any>"); assertNotSubtype("Derived_T<Int>", "Base_T<Any>");
@@ -212,6 +229,9 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
assertSubtype("Nothing", "Int"); assertSubtype("Nothing", "Int");
assertSubtype("Nothing?", "Int?"); assertSubtype("Nothing?", "Int?");
assertNotSubtype("Nothing?", "Int"); assertNotSubtype("Nothing?", "Int");
assertSubtype("Nothing?", "Base_T<*>?");
assertSubtype("Nothing?", "Derived_T<*>?");
} }
public void testImplicitConversions() throws Exception { public void testImplicitConversions() throws Exception {
@@ -227,8 +247,8 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
} }
private static void assertSubtypingRelation(String type1, String type2, boolean expected) { private static void assertSubtypingRelation(String type1, String type2, boolean expected) {
Type typeNode1 = TypeResolver.INSTANCE.resolveType(ClassDefinitions.BASIC_SCOPE, JetChangeUtil.createType(getProject(), type1)); Type typeNode1 = makeType(type1);
Type typeNode2 = TypeResolver.INSTANCE.resolveType(ClassDefinitions.BASIC_SCOPE, JetChangeUtil.createType(getProject(), type2)); Type typeNode2 = makeType(type2);
boolean result = JetTypeChecker.INSTANCE.isSubtypeOf( boolean result = JetTypeChecker.INSTANCE.isSubtypeOf(
typeNode1, typeNode1,
typeNode2); typeNode2);
@@ -253,15 +273,29 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
private static void assertType(String expression, Type expectedType) { private static void assertType(String expression, Type expectedType) {
Project project = getProject(); Project project = getProject();
JetExpression jetExpression = JetChangeUtil.createExpression(project, expression); 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)); 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 class ClassDefinitions {
private static Map<String, ClassDescriptor> CLASSES = new HashMap<String, ClassDescriptor>(); private static Map<String, ClassDescriptor> CLASSES = new HashMap<String, ClassDescriptor>();
private static String[] CLASS_DECLARATIONS = { private static String[] CLASS_DECLARATIONS = {
"class Base_T<T>", "class Base_T<T>",
"class Derived_T<T> : 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 Base_inT<in T>",
"class Derived_inT<in T> : Base_inT<T>", "class Derived_inT<in T> : Base_inT<T>",
"class Base_outT<out T>", "class Base_outT<out T>",