From 7f74b8380f22182ea0cb8e75ca108fb9018d4fe8 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 27 Jan 2011 20:02:23 +0300 Subject: [PATCH] Common supertypes found correctly for simple cases --- .../jet/lang/types/JetStandardClasses.java | 4 + .../jet/lang/types/JetTypeChecker.java | 86 ++++++++++++++++--- .../jet/types/JetTypeCheckerTest.java | 8 ++ 3 files changed, 85 insertions(+), 13 deletions(-) diff --git a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java index 983dbed30d9..49af3c11ba9 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java @@ -207,6 +207,10 @@ public class JetStandardClasses { return NULLABLE_NOTHING_TYPE; } + public static boolean isNothing(Type type) { + return type.getConstructor() == NOTHING_CLASS.getTypeConstructor(); + } + public static Type getTupleType(List attributes, List arguments) { if (attributes.isEmpty() && arguments.isEmpty()) { return getUnitType(); diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index 359f3ebb1a9..a4f8ea17062 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -6,10 +6,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.JetNodeTypes; import org.jetbrains.jet.lang.psi.*; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * @author abreslav @@ -18,18 +15,24 @@ public class JetTypeChecker { public static final JetTypeChecker INSTANCE = new JetTypeChecker(); /* - : jump : if : when : try - : functionLiteral + + : SimpleName : "this" ("<" type ">")? - : "typeof" "(" expression ")" + : "new" constructorInvocation + : objectLiteral + + : "typeof" "(" expression ")" + : declaration : loop - : SimpleName + + : functionLiteral + : "namespace" // for the root namespace */ public Type getType(JetExpression expression) { @@ -94,6 +97,21 @@ public class JetTypeChecker { 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 public void visitTupleExpression(JetTupleExpression expression) { List entries = expression.getEntries(); @@ -113,6 +131,49 @@ public class JetTypeChecker { return result[0]; } + private Type commonSupertype(Type thenType, Type elseType) { + if (JetStandardClasses.isNothing(thenType)) { + return elseType; + } + if (JetStandardClasses.isNothing(elseType)) { + return thenType; + } + + List thenOrder = new LinkedList(); // adding to the beginning + Map visited = new HashMap(); + topologicallySort(thenType, visited, thenOrder, null); + + List elseOrder = new LinkedList(); // adding to the beginning + topologicallySort(elseType, new HashMap(), elseOrder, visited); + + assert !elseOrder.isEmpty() : "No common supertype"; + // TODO: support multiple common supertypes + + return elseOrder.get(0); + } + + private void topologicallySort(Type current, Map visited, List topologicalOrder, @Nullable Map filter) { + Type visitedOccurrence = visited.put(current.getConstructor(), current); + if (visitedOccurrence != null) { + assert equalTypes(visitedOccurrence, current); + return; + } + Map 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) { throw new UnsupportedOperationException(); // TODO } @@ -121,7 +182,7 @@ public class JetTypeChecker { if (!supertype.isNullable() && subtype.isNullable()) { return false; } - if (subtype.getConstructor() == JetStandardClasses.getNothing().getTypeConstructor()) { + if (JetStandardClasses.isNothing(subtype)) { return true; } @Nullable Type closestSupertype = findCorrespondingSupertype(subtype, supertype); @@ -143,13 +204,13 @@ public class JetTypeChecker { for (Type immediateSupertype : constructor.getSupertypes()) { Type correspondingSupertype = findCorrespondingSupertype(immediateSupertype, supertype); if (correspondingSupertype != null) { - return substituteForParameters(subtype, correspondingSupertype); + return substitute(getSubstitutionContext(subtype), correspondingSupertype); } } return null; } - private Type substituteForParameters(Type context, Type subject) { + private Map getSubstitutionContext(Type context) { Map parameterValues = new HashMap(); List parameters = context.getConstructor().getParameters(); @@ -159,8 +220,7 @@ public class JetTypeChecker { TypeProjection value = contextArguments.get(i); parameterValues.put(parameter.getTypeConstructor(), value); } - - return substitute(parameterValues, subject); + return parameterValues; } @NotNull diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index 053c780ce09..ef4762cc4ed 100644 --- a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -76,6 +76,14 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { 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 { assertSubtype("Boolean", "Boolean"); assertSubtype("Byte", "Byte");