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