From 113a066e48562b4700ec6ac545c0e0d16e94c682 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 20 Jan 2011 19:28:36 +0300 Subject: [PATCH] Working on the type checker --- grammar/src/types.grm | 12 +++- .../jet/lang/annotations/JetPsiChecker.java | 7 +- .../lang/parsing/JetExpressionParsing.java | 5 ++ .../SemanticWhitespaceAwarePsiBuilder.java | 2 +- .../jetbrains/jet/lang/psi/JetChangeUtil.java | 5 ++ .../jetbrains/jet/lang/psi/JetElement.java | 2 +- .../jet/lang/psi/JetTypeReference.java | 14 ++++ .../jet/lang/types/JetTypeChecker.java | 5 ++ .../jet/lang/types/NamespaceDescriptor.java | 7 ++ .../jetbrains/jet/lang/types/ThisType.java | 28 ++++++++ .../jet/lang/types/TypeProjection.java | 4 ++ .../testData/psi/examples/util/Comparison.jet | 2 +- .../psi/examples/util/IComparable.jet | 2 +- .../JetPsiCheckerTest.java | 3 +- .../JetTypeCheckerTest.java | 69 +++++++++++++++++-- 15 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/lang/types/NamespaceDescriptor.java create mode 100644 idea/src/org/jetbrains/jet/lang/types/ThisType.java rename idea/tests/org/jetbrains/jet/{parsing => checkers}/JetPsiCheckerTest.java (89%) rename idea/tests/org/jetbrains/jet/{parsing => types}/JetTypeCheckerTest.java (56%) diff --git a/grammar/src/types.grm b/grammar/src/types.grm index 6c77b3848ca..3e9646e521e 100644 --- a/grammar/src/types.grm +++ b/grammar/src/types.grm @@ -10,9 +10,19 @@ Foo, T, Object> // user type */ type - : attributes (selfType | functionType | userType | tupleType) + : attributes typeDescriptor + +typeDescriptor + : selfType + : functionType + : userType + : tupleType + : nullableType ; +nullableType + : typeDescriptor "?" + selfType : "This" ; diff --git a/idea/src/org/jetbrains/jet/lang/annotations/JetPsiChecker.java b/idea/src/org/jetbrains/jet/lang/annotations/JetPsiChecker.java index fdf3e9bf7b6..1dc32aca633 100644 --- a/idea/src/org/jetbrains/jet/lang/annotations/JetPsiChecker.java +++ b/idea/src/org/jetbrains/jet/lang/annotations/JetPsiChecker.java @@ -1,7 +1,6 @@ package org.jetbrains.jet.lang.annotations; import com.intellij.lang.annotation.AnnotationHolder; -import com.intellij.lang.annotation.AnnotationSession; import com.intellij.lang.annotation.Annotator; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; @@ -21,7 +20,7 @@ public class JetPsiChecker implements Annotator { // holder.createErrorAnnotation(property.getNameIdentifier(), "Specify either type or value"); JetFile file = (JetFile) element; String path = file.getProject().getBaseDir().getPath(); - System.out.println("Path: " + path); + println("Path: " + path); JetNamespace rootNamespace = file.getRootNamespace(); List declarations = rootNamespace.getDeclarations(); @@ -54,10 +53,10 @@ public class JetPsiChecker implements Annotator { } private void print(Object o) { - System.out.print(o); +// System.out.print(o); } private void println(Object o) { - System.out.println(o); +// System.out.println(o); } } diff --git a/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java b/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java index 12bdb8e8c91..9afb0b217ee 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java @@ -141,6 +141,11 @@ public class JetExpressionParsing extends AbstractJetParsing { * ; */ public void parseExpression() { + // TODO: better recovery for expressions + if (atSet(RPAR, RBRACE, RBRACKET, GT)) { + error("Expecting an expression"); + return; + } parseBinaryExpression(Precedence.ASSIGNMENT); } diff --git a/idea/src/org/jetbrains/jet/lang/parsing/SemanticWhitespaceAwarePsiBuilder.java b/idea/src/org/jetbrains/jet/lang/parsing/SemanticWhitespaceAwarePsiBuilder.java index 52bad9016b6..127bc633677 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/SemanticWhitespaceAwarePsiBuilder.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/SemanticWhitespaceAwarePsiBuilder.java @@ -6,7 +6,7 @@ import com.intellij.lang.PsiBuilder; * @author abreslav */ public interface SemanticWhitespaceAwarePsiBuilder extends PsiBuilder { - // TODO: comments go to wrong place when an empty element is created + // TODO: comments go to wrong place when an empty element is created, see IElementType.isLeftBound() boolean newlineBeforeCurrentToken(); void disableNewlines(); diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetChangeUtil.java b/idea/src/org/jetbrains/jet/lang/psi/JetChangeUtil.java index 685a93c096e..3b60e625fdc 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetChangeUtil.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetChangeUtil.java @@ -18,6 +18,11 @@ public class JetChangeUtil { return property.getInitializer(); } + public static JetTypeReference createType(Project project, String type) { + JetProperty property = createProperty(project, "val x : " + type); + return property.getPropertyTypeRef(); + } + @NotNull public static JetFile createFile(Project project, String text) { return (JetFile) PsiFileFactory.getInstance(project).createFileFromText("dummy.jet", JetFileType.INSTANCE, text, LocalTimeCounter.currentTime(), true); diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetElement.java b/idea/src/org/jetbrains/jet/lang/psi/JetElement.java index 692e34a21c2..a2555ad655f 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetElement.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetElement.java @@ -48,7 +48,7 @@ public class JetElement extends ASTWrapperPsiElement { } } - public void accept(JetVisitor visitor) { + public void accept(@NotNull JetVisitor visitor) { visitor.visitJetElement(this); } } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetTypeReference.java b/idea/src/org/jetbrains/jet/lang/psi/JetTypeReference.java index 372a8f09b5c..8bada79ef17 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetTypeReference.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetTypeReference.java @@ -25,6 +25,20 @@ public class JetTypeReference extends JetElement { return findChildrenByType(JetNodeTypes.ATTRIBUTE_ANNOTATION); } + public JetTypeElement getTypeElement() { + return findChildByClass(JetTypeElement.class); + } + + public JetTypeArgumentList getTypeArgumentList() { + return findChildByClass(JetTypeArgumentList.class); + } + + public List getTypeArguments() { + // TODO: empty elements in PSI + JetTypeArgumentList typeArgumentList = getTypeArgumentList(); + return typeArgumentList == null ? Collections.emptyList() : typeArgumentList.getArguments(); + } + public List getAttributes() { List answer = null; for (JetAttributeAnnotation annotation : getAttributeAnnotations()) { diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index 051f63a2d0a..42f49248e8e 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -62,4 +62,9 @@ public class JetTypeChecker { public boolean isConvertibleTo(JetExpression expression, Type type) { return false; //To change body of created methods use File | Settings | File Templates. } + + public boolean isSubtypeOf(Type subtype, Type supertype) { + + return false; + } } diff --git a/idea/src/org/jetbrains/jet/lang/types/NamespaceDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/NamespaceDescriptor.java new file mode 100644 index 00000000000..153f1426ef0 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/types/NamespaceDescriptor.java @@ -0,0 +1,7 @@ +package org.jetbrains.jet.lang.types; + +/** + * @author abreslav + */ +public class NamespaceDescriptor { +} diff --git a/idea/src/org/jetbrains/jet/lang/types/ThisType.java b/idea/src/org/jetbrains/jet/lang/types/ThisType.java new file mode 100644 index 00000000000..8135e1ac977 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/types/ThisType.java @@ -0,0 +1,28 @@ +package org.jetbrains.jet.lang.types; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * @author abreslav + */ +public class ThisType extends TypeImpl { + public ThisType(List annotations, ClassDescriptor thisClass) { + super(annotations, thisClass.getTypeConstructor(), toArguments(thisClass.getTypeConstructor().getParameters())); + } + + private static List toArguments(List parameters) { + List result = new ArrayList(); + for (TypeParameterDescriptor parameter : parameters) { + result.add(new TypeProjection(new TypeVariable(Collections.emptyList(), parameter))); + } + return result; + } + + @Override + public Collection getMembers() { + throw new UnsupportedOperationException(); // TODO + } +} diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeProjection.java b/idea/src/org/jetbrains/jet/lang/types/TypeProjection.java index 89c498fc417..7a11f8fad4c 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeProjection.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeProjection.java @@ -12,6 +12,10 @@ public class TypeProjection { this.type = type; } + public TypeProjection(Type type) { + this(Variance.INVARIANT, type); + } + public Variance getProjection() { return projection; } diff --git a/idea/testData/psi/examples/util/Comparison.jet b/idea/testData/psi/examples/util/Comparison.jet index 12a9fdc35c0..bda2f4de89b 100644 --- a/idea/testData/psi/examples/util/Comparison.jet +++ b/idea/testData/psi/examples/util/Comparison.jet @@ -1,6 +1,6 @@ type Comparison = {(T, T) : Int} -fun naturalOrder>(a : T, b : T) : Int = a.compareTo(b) +fun naturalOrder>(a : T, b : T) : Int = a.compareTo(b) fun castingNaturalOrder(a : Object, b : Object) : Int = (a as Comparable).compareTo(b as Comparable) diff --git a/idea/testData/psi/examples/util/IComparable.jet b/idea/testData/psi/examples/util/IComparable.jet index 79d19a364e6..b802c381464 100644 --- a/idea/testData/psi/examples/util/IComparable.jet +++ b/idea/testData/psi/examples/util/IComparable.jet @@ -1,3 +1,3 @@ -interface class IComparable { +virtual class IComparable { fun compareTo(other : T) : Int } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/parsing/JetPsiCheckerTest.java b/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java similarity index 89% rename from idea/tests/org/jetbrains/jet/parsing/JetPsiCheckerTest.java rename to idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java index 126a82a1b4b..ac6fe131e9d 100644 --- a/idea/tests/org/jetbrains/jet/parsing/JetPsiCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java @@ -1,7 +1,8 @@ -package org.jetbrains.jet.parsing; +package org.jetbrains.jet.checkers; import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; import com.intellij.openapi.application.PathManager; +import org.jetbrains.jet.parsing.JetParsingTest; import java.io.File; diff --git a/idea/tests/org/jetbrains/jet/parsing/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java similarity index 56% rename from idea/tests/org/jetbrains/jet/parsing/JetTypeCheckerTest.java rename to idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index c09949eaf00..f8e9c9977b0 100644 --- a/idea/tests/org/jetbrains/jet/parsing/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -1,15 +1,16 @@ -package org.jetbrains.jet.parsing; +package org.jetbrains.jet.types; import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.project.Project; -import org.jetbrains.jet.lang.psi.JetChangeUtil; -import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.types.JetStandardTypes; -import org.jetbrains.jet.lang.types.Type; import org.jetbrains.jet.lang.types.JetTypeChecker; +import org.jetbrains.jet.lang.types.Type; +import org.jetbrains.jet.parsing.JetParsingTest; import java.io.File; +import java.util.List; /** * @author abreslav @@ -55,6 +56,66 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { assertType("()", JetStandardTypes.getUnit()); } + public void testSubtyping() throws Exception { + assertSubtype("Boolean", "Boolean"); + assertSubtype("Byte", "Byte"); + assertSubtype("Char", "Char"); + assertSubtype("Short", "Short"); + assertSubtype("Int", "Int"); + assertSubtype("Long", "Long"); + assertSubtype("Float", "Float"); + assertSubtype("Double", "Double"); + assertSubtype("Unit", "Unit"); + + assertNotSubtype("Boolean", "Byte"); + assertNotSubtype("Byte", "Short"); + assertNotSubtype("Char", "Int"); + assertNotSubtype("Short", "Int"); + assertNotSubtype("Int", "Long"); + assertNotSubtype("Long", "Double"); + assertNotSubtype("Float", "Double"); + assertNotSubtype("Double", "Int"); + assertNotSubtype("Unit", "Unit"); + + assertSubtype("(Boolean)", "(Boolean)"); + assertSubtype("(Byte)", "(Byte)"); + assertSubtype("(Char)", "(Char)"); + assertSubtype("(Short)", "(Short)"); + assertSubtype("(Int)", "(Int)"); + assertSubtype("(Long)", "(Long)"); + assertSubtype("(Float)", "(Float)"); + assertSubtype("(Double)", "(Double)"); + assertSubtype("(Unit)", "(Unit)"); + + } + + private void assertSubtype(String type1, String type2) { + assertSubtypingRelation(type1, type2, true); + } + + private void assertNotSubtype(String type1, String type2) { + assertSubtypingRelation(type1, type2, false); + } + + private void assertSubtypingRelation(String type1, String type2, boolean expected) { + Type typeNode1 = toType(JetChangeUtil.createType(getProject(), type1)); + Type typeNode2 = toType(JetChangeUtil.createType(getProject(), type2)); + boolean result = new JetTypeChecker().isSubtypeOf( + typeNode1, + typeNode2); + assertTrue(typeNode1 + " is not a subtype of " + typeNode2, result == expected); + } + + private Type toType(JetTypeReference typeNode) { + List attributes = typeNode.getAttributes(); + JetTypeElement typeElement = typeNode.getTypeElement(); + List typeArguments = typeNode.getTypeArguments(); + + typeElement.accept(new JetVisitor()); + + throw new UnsupportedOperationException(); // TODO + } + public void testImplicitConversions() throws Exception { assertConvertibleTo("1", JetStandardTypes.getByte()); }