Common supertypes found correctly for simple cases
This commit is contained in:
@@ -207,6 +207,10 @@ public class JetStandardClasses {
|
|||||||
return NULLABLE_NOTHING_TYPE;
|
return NULLABLE_NOTHING_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isNothing(Type type) {
|
||||||
|
return type.getConstructor() == NOTHING_CLASS.getTypeConstructor();
|
||||||
|
}
|
||||||
|
|
||||||
public static Type getTupleType(List<Attribute> attributes, List<Type> arguments) {
|
public static Type getTupleType(List<Attribute> attributes, List<Type> arguments) {
|
||||||
if (attributes.isEmpty() && arguments.isEmpty()) {
|
if (attributes.isEmpty() && arguments.isEmpty()) {
|
||||||
return getUnitType();
|
return getUnitType();
|
||||||
|
|||||||
@@ -6,10 +6,7 @@ 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 java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author abreslav
|
* @author abreslav
|
||||||
@@ -18,18 +15,24 @@ public class JetTypeChecker {
|
|||||||
public static final JetTypeChecker INSTANCE = new JetTypeChecker();
|
public static final JetTypeChecker INSTANCE = new JetTypeChecker();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
: jump
|
|
||||||
: if
|
: if
|
||||||
: when
|
: when
|
||||||
: try
|
: try
|
||||||
: functionLiteral
|
|
||||||
|
: SimpleName
|
||||||
: "this" ("<" type ">")?
|
: "this" ("<" type ">")?
|
||||||
: "typeof" "(" expression ")"
|
|
||||||
: "new" constructorInvocation
|
: "new" constructorInvocation
|
||||||
|
|
||||||
: objectLiteral
|
: objectLiteral
|
||||||
|
|
||||||
|
: "typeof" "(" expression ")"
|
||||||
|
|
||||||
: declaration
|
: declaration
|
||||||
: loop
|
: loop
|
||||||
: SimpleName
|
|
||||||
|
: functionLiteral
|
||||||
|
|
||||||
: "namespace" // for the root namespace
|
: "namespace" // for the root namespace
|
||||||
*/
|
*/
|
||||||
public Type getType(JetExpression expression) {
|
public Type getType(JetExpression expression) {
|
||||||
@@ -94,6 +97,21 @@ public class JetTypeChecker {
|
|||||||
throw new UnsupportedOperationException("Return some reflection interface"); // TODO
|
throw new UnsupportedOperationException("Return some reflection interface"); // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitIfExpression(JetIfExpression expression) {
|
||||||
|
// TODO : check condition type
|
||||||
|
// TODO : change types according to is and nullability according to null comparisons
|
||||||
|
JetExpression elseBranch = expression.getElse();
|
||||||
|
if (elseBranch == null) {
|
||||||
|
// TODO : type-check the branch
|
||||||
|
result[0] = JetStandardClasses.getUnitType();
|
||||||
|
} else {
|
||||||
|
Type thenType = getType(expression.getThen());
|
||||||
|
Type elseType = getType(elseBranch);
|
||||||
|
result[0] = commonSupertype(thenType, elseType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitTupleExpression(JetTupleExpression expression) {
|
public void visitTupleExpression(JetTupleExpression expression) {
|
||||||
List<JetExpression> entries = expression.getEntries();
|
List<JetExpression> entries = expression.getEntries();
|
||||||
@@ -113,6 +131,49 @@ public class JetTypeChecker {
|
|||||||
return result[0];
|
return result[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Type commonSupertype(Type thenType, Type elseType) {
|
||||||
|
if (JetStandardClasses.isNothing(thenType)) {
|
||||||
|
return elseType;
|
||||||
|
}
|
||||||
|
if (JetStandardClasses.isNothing(elseType)) {
|
||||||
|
return thenType;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Type> thenOrder = new LinkedList<Type>(); // adding to the beginning
|
||||||
|
Map<TypeConstructor, Type> visited = new HashMap<TypeConstructor, Type>();
|
||||||
|
topologicallySort(thenType, visited, thenOrder, null);
|
||||||
|
|
||||||
|
List<Type> elseOrder = new LinkedList<Type>(); // adding to the beginning
|
||||||
|
topologicallySort(elseType, new HashMap<TypeConstructor, Type>(), elseOrder, visited);
|
||||||
|
|
||||||
|
assert !elseOrder.isEmpty() : "No common supertype";
|
||||||
|
// TODO: support multiple common supertypes
|
||||||
|
|
||||||
|
return elseOrder.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void topologicallySort(Type current, Map<TypeConstructor, Type> visited, List<Type> topologicalOrder, @Nullable Map<TypeConstructor, Type> filter) {
|
||||||
|
Type visitedOccurrence = visited.put(current.getConstructor(), current);
|
||||||
|
if (visitedOccurrence != null) {
|
||||||
|
assert equalTypes(visitedOccurrence, current);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Map<TypeConstructor, TypeProjection> substitutionContext = getSubstitutionContext(current);
|
||||||
|
for (Type supertype : current.getConstructor().getSupertypes()) {
|
||||||
|
TypeConstructor supertypeConstructor = supertype.getConstructor();
|
||||||
|
if (visited.containsKey(supertypeConstructor)) {
|
||||||
|
assert equalTypes(visited.get(supertypeConstructor), supertype);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Type substitutedSupertype = substitute(substitutionContext, supertype);
|
||||||
|
topologicallySort(substitutedSupertype, visited, topologicalOrder, filter);
|
||||||
|
}
|
||||||
|
if (filter != null && filter.containsKey(current.getConstructor())) {
|
||||||
|
assert equalTypes(filter.get(current.getConstructor()), current);
|
||||||
|
topologicalOrder.add(0, current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isConvertibleTo(JetExpression expression, Type type) {
|
public boolean isConvertibleTo(JetExpression expression, Type type) {
|
||||||
throw new UnsupportedOperationException(); // TODO
|
throw new UnsupportedOperationException(); // TODO
|
||||||
}
|
}
|
||||||
@@ -121,7 +182,7 @@ public class JetTypeChecker {
|
|||||||
if (!supertype.isNullable() && subtype.isNullable()) {
|
if (!supertype.isNullable() && subtype.isNullable()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (subtype.getConstructor() == JetStandardClasses.getNothing().getTypeConstructor()) {
|
if (JetStandardClasses.isNothing(subtype)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@Nullable Type closestSupertype = findCorrespondingSupertype(subtype, supertype);
|
@Nullable Type closestSupertype = findCorrespondingSupertype(subtype, supertype);
|
||||||
@@ -143,13 +204,13 @@ 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 substituteForParameters(subtype, correspondingSupertype);
|
return substitute(getSubstitutionContext(subtype), correspondingSupertype);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Type substituteForParameters(Type context, Type subject) {
|
private Map<TypeConstructor, TypeProjection> getSubstitutionContext(Type context) {
|
||||||
Map<TypeConstructor, TypeProjection> parameterValues = new HashMap<TypeConstructor, TypeProjection>();
|
Map<TypeConstructor, TypeProjection> parameterValues = new HashMap<TypeConstructor, TypeProjection>();
|
||||||
|
|
||||||
List<TypeParameterDescriptor> parameters = context.getConstructor().getParameters();
|
List<TypeParameterDescriptor> parameters = context.getConstructor().getParameters();
|
||||||
@@ -159,8 +220,7 @@ public class JetTypeChecker {
|
|||||||
TypeProjection value = contextArguments.get(i);
|
TypeProjection value = contextArguments.get(i);
|
||||||
parameterValues.put(parameter.getTypeConstructor(), value);
|
parameterValues.put(parameter.getTypeConstructor(), value);
|
||||||
}
|
}
|
||||||
|
return parameterValues;
|
||||||
return substitute(parameterValues, subject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -76,6 +76,14 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
|
|||||||
assertType("break \"foo\"", JetStandardClasses.getNothingType());
|
assertType("break \"foo\"", JetStandardClasses.getNothingType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
public void testBasicSubtyping() throws Exception {
|
public void testBasicSubtyping() throws Exception {
|
||||||
assertSubtype("Boolean", "Boolean");
|
assertSubtype("Boolean", "Boolean");
|
||||||
assertSubtype("Byte", "Byte");
|
assertSubtype("Byte", "Byte");
|
||||||
|
|||||||
Reference in New Issue
Block a user