From 87ba0420053cc2b814c28874220257e283e8fff7 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 3 Feb 2011 18:01:45 +0300 Subject: [PATCH] Types for this and loops --- .../jet/lang/psi/JetDoWhileExpression.java | 6 +-- .../jet/lang/psi/JetForExpression.java | 6 +-- .../jet/lang/psi/JetLoopExpression.java | 20 +++++++++ .../jetbrains/jet/lang/psi/JetVisitor.java | 10 +++-- .../jet/lang/psi/JetWhileExpression.java | 7 +-- .../jetbrains/jet/lang/resolve/JetScope.java | 3 ++ .../jet/lang/resolve/JetScopeAdapter.java | 6 ++- .../jet/lang/resolve/JetScopeImpl.java | 5 +++ .../jet/lang/types/ClassDescriptor.java | 2 + .../jet/lang/types/JetTypeChecker.java | 38 ++++++++++++---- .../jet/types/JetTypeCheckerTest.java | 43 +++++++++++++++---- 11 files changed, 110 insertions(+), 36 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/lang/psi/JetLoopExpression.java diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetDoWhileExpression.java b/idea/src/org/jetbrains/jet/lang/psi/JetDoWhileExpression.java index 153b429aed4..a7aedd2a3ca 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetDoWhileExpression.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetDoWhileExpression.java @@ -8,7 +8,7 @@ import org.jetbrains.jet.JetNodeTypes; /** * @author max */ -public class JetDoWhileExpression extends JetExpression { +public class JetDoWhileExpression extends JetLoopExpression { public JetDoWhileExpression(@NotNull ASTNode node) { super(node); } @@ -23,8 +23,4 @@ public class JetDoWhileExpression extends JetExpression { return findExpressionUnder(JetNodeTypes.CONDITION); } - @Nullable - public JetExpression getBody() { - return findExpressionUnder(JetNodeTypes.BODY); - } } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetForExpression.java b/idea/src/org/jetbrains/jet/lang/psi/JetForExpression.java index 826f60c7a50..423ad6c619a 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetForExpression.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetForExpression.java @@ -7,7 +7,7 @@ import org.jetbrains.jet.JetNodeTypes; /** * @author max */ -public class JetForExpression extends JetExpression { +public class JetForExpression extends JetLoopExpression { public JetForExpression(@NotNull ASTNode node) { super(node); } @@ -24,8 +24,4 @@ public class JetForExpression extends JetExpression { public JetExpression getLoopRange() { return findExpressionUnder(JetNodeTypes.LOOP_RANGE); } - - public JetExpression getBody() { - return findExpressionUnder(JetNodeTypes.BODY); - } } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetLoopExpression.java b/idea/src/org/jetbrains/jet/lang/psi/JetLoopExpression.java new file mode 100644 index 00000000000..1bf26d49171 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/psi/JetLoopExpression.java @@ -0,0 +1,20 @@ +package org.jetbrains.jet.lang.psi; + +import com.intellij.lang.ASTNode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.JetNodeTypes; + +/** + * @author abreslav + */ +public abstract class JetLoopExpression extends JetExpression { + public JetLoopExpression(@NotNull ASTNode node) { + super(node); + } + + @Nullable + public JetExpression getBody() { + return findExpressionUnder(JetNodeTypes.BODY); + } +} diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetVisitor.java b/idea/src/org/jetbrains/jet/lang/psi/JetVisitor.java index 48a3d46695d..9022952736d 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetVisitor.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetVisitor.java @@ -134,6 +134,10 @@ public class JetVisitor extends PsiElementVisitor { visitJetElement(expression); } + public void visitLoopExpression(JetLoopExpression loopExpression) { + visitExpression(loopExpression); + } + public void visitConstantExpression(JetConstantExpression expression) { visitExpression(expression); } @@ -195,15 +199,15 @@ public class JetVisitor extends PsiElementVisitor { } public void visitForExpression(JetForExpression expression) { - visitExpression(expression); + visitLoopExpression(expression); } public void visitWhileExpression(JetWhileExpression expression) { - visitExpression(expression); + visitLoopExpression(expression); } public void visitDoWhileExpression(JetDoWhileExpression expression) { - visitExpression(expression); + visitLoopExpression(expression); } public void visitFunctionLiteralExpression(JetFunctionLiteralExpression expression) { diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetWhileExpression.java b/idea/src/org/jetbrains/jet/lang/psi/JetWhileExpression.java index 1370d5c555d..23cecaa7f74 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetWhileExpression.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetWhileExpression.java @@ -8,7 +8,7 @@ import org.jetbrains.jet.JetNodeTypes; /** * @author max */ -public class JetWhileExpression extends JetExpression { +public class JetWhileExpression extends JetLoopExpression { public JetWhileExpression(@NotNull ASTNode node) { super(node); } @@ -22,9 +22,4 @@ public class JetWhileExpression extends JetExpression { public JetExpression getCondition() { return findExpressionUnder(JetNodeTypes.CONDITION); } - - @Nullable - public JetExpression getBody() { - return findExpressionUnder(JetNodeTypes.BODY); - } } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/JetScope.java b/idea/src/org/jetbrains/jet/lang/resolve/JetScope.java index e2fe5cfd910..817e6a65e53 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/JetScope.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/JetScope.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.lang.resolve; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.modules.MemberDomain; import org.jetbrains.jet.lang.modules.NamespaceDomain; @@ -14,4 +15,6 @@ public interface JetScope extends NamespaceDomain, MemberDomain { @Nullable TypeParameterDescriptor getTypeParameterDescriptor(String name); + @NotNull + Type getThisType(); } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/JetScopeAdapter.java b/idea/src/org/jetbrains/jet/lang/resolve/JetScopeAdapter.java index 72ed35a3f69..339f18b972c 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/JetScopeAdapter.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/JetScopeAdapter.java @@ -6,13 +6,17 @@ import org.jetbrains.jet.lang.types.*; * @author abreslav */ public class JetScopeAdapter implements JetScope { - private final JetScope scope; public JetScopeAdapter(JetScope scope) { this.scope = scope; } + @Override + public Type getThisType() { + return scope.getThisType(); + } + @Override public TypeParameterDescriptor getTypeParameterDescriptor(String name) { return scope.getTypeParameterDescriptor(name); diff --git a/idea/src/org/jetbrains/jet/lang/resolve/JetScopeImpl.java b/idea/src/org/jetbrains/jet/lang/resolve/JetScopeImpl.java index 81c5f2e5211..72dc863ab8b 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/JetScopeImpl.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/JetScopeImpl.java @@ -35,4 +35,9 @@ public abstract class JetScopeImpl implements JetScope { public TypeParameterDescriptor getTypeParameterDescriptor(String name) { return null; } + + @Override + public Type getThisType() { + return JetStandardClasses.getNothingType(); + } } diff --git a/idea/src/org/jetbrains/jet/lang/types/ClassDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/ClassDescriptor.java index 3adffb0e183..33817545fac 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ClassDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/types/ClassDescriptor.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.lang.types; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.modules.MemberDomain; import java.util.Collection; @@ -25,6 +26,7 @@ public class ClassDescriptor extends MemberDescriptorImpl implements MemberDomai Collections.singleton(JetStandardClasses.getAnyType())); } + @NotNull public TypeConstructor getTypeConstructor() { return typeConstructor; } diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index 05309938192..f5ceb45ac05 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -18,22 +18,17 @@ public class JetTypeChecker { public static final JetTypeChecker INSTANCE = new JetTypeChecker(); /* - : try - - : SimpleName - : "this" ("<" type ">")? - : "new" constructorInvocation : objectLiteral - : "typeof" "(" expression ")" + : SimpleName - : declaration - : loop + : "typeof" "(" expression ")" : functionLiteral + : declaration : "namespace" // for the root namespace */ public Type getType(@NotNull final JetScope scope, @NotNull JetExpression expression) { @@ -166,6 +161,28 @@ public class JetTypeChecker { result[0] = JetStandardClasses.getTupleType(types); } + @Override + public void visitThisExpression(JetThisExpression expression) { + // TODO : qualified this, e.g. Foo.this + Type thisType = scope.getThisType(); + JetTypeReference superTypeQualifier = expression.getSuperTypeQualifier(); + if (superTypeQualifier != null) { + // This cast must be safe (assuming the PSI doesn't contain errors) + JetUserType typeElement = (JetUserType) superTypeQualifier.getTypeElement(); + ClassDescriptor superclass = TypeResolver.INSTANCE.resolveClass(scope, typeElement); + Collection supertypes = thisType.getConstructor().getSupertypes(); + for (Type declaredSupertype : supertypes) { + if (declaredSupertype.getConstructor().equals(superclass.getTypeConstructor())) { + result[0] = substituteInType(getSubstitutionContext(thisType), declaredSupertype); + break; + } + } + assert result[0] != null; + } else { + result[0] = thisType; + } + } + @Override public void visitBlockExpression(JetBlockExpression expression) { // TODO : this is a stub, consider function literals @@ -177,6 +194,11 @@ public class JetTypeChecker { } } + @Override + public void visitLoopExpression(JetLoopExpression expression) { + result[0] = JetStandardClasses.getUnitType(); + } + @Override public void visitJetElement(JetElement elem) { throw new IllegalArgumentException("Unsupported element: " + elem); diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index ff5e8972264..94010565f52 100644 --- a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -4,10 +4,7 @@ import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.project.Project; import org.jetbrains.jet.lang.psi.*; -import org.jetbrains.jet.lang.resolve.ClassDescriptorResolver; -import org.jetbrains.jet.lang.resolve.JetScope; -import org.jetbrains.jet.lang.resolve.JetScopeImpl; -import org.jetbrains.jet.lang.resolve.TypeResolver; +import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.parsing.JetParsingTest; @@ -170,10 +167,10 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { assertSubtype("()", "()"); assertSubtype("(Boolean)", "(Boolean)"); - assertSubtype("(Byte)", "(Byte)"); + assertSubtype("(Byte)", "(Byte)"); assertSubtype("(Char)", "(Char)"); assertSubtype("(Short)", "(Short)"); - assertSubtype("(Int)", "(Int)"); + assertSubtype("(Int)", "(Int)"); assertSubtype("(Long)", "(Long)"); assertSubtype("(Float)", "(Float)"); assertSubtype("(Double)", "(Double)"); @@ -266,6 +263,17 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { assertSubtype("Nothing?", "Derived_T<*>?"); } + public void testThis() throws Exception { + assertType("Derived_T", "this", "Derived_T"); + assertType("Derived_T", "this", "Base_T"); + } + + public void testLoops() throws Exception { + assertType("while (1) {1}", "Unit"); + assertType("do {1} while(1)", "Unit"); + assertType("for (i in 1) {1}", "Unit"); + } + public void testImplicitConversions() throws Exception { assertConvertibleTo("1", JetStandardClasses.getByteType()); } @@ -318,16 +326,35 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { assertTrue(type + " != " + expectedType, JetTypeChecker.INSTANCE.equalTypes(type, expectedType)); } + private void assertType(String contextType, String expression, String expectedType) { + final Type thisType = makeType(contextType); + JetScope scope = new JetScopeAdapter(ClassDefinitions.BASIC_SCOPE) { + @Override + public Type getThisType() { + return thisType; + } + }; + assertType(scope, expression, expectedType); + } + private static void assertType(String expression, String expectedTypeStr) { + assertType(ClassDefinitions.BASIC_SCOPE, expression, expectedTypeStr); + } + + private static void assertType(JetScope scope, String expression, String expectedTypeStr) { Project project = getProject(); JetExpression jetExpression = JetChangeUtil.createExpression(project, expression); - Type type = JetTypeChecker.INSTANCE.getType(ClassDefinitions.BASIC_SCOPE, jetExpression); + Type type = JetTypeChecker.INSTANCE.getType(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)); + return makeType(ClassDefinitions.BASIC_SCOPE, typeStr); + } + + private static Type makeType(JetScope scope, String typeStr) { + return TypeResolver.INSTANCE.resolveType(scope, JetChangeUtil.createType(getProject(), typeStr)); } private static class ClassDefinitions {