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:
@@ -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>",
|
||||
|
||||
Reference in New Issue
Block a user