From 1c36c9be1687886354f14f73fcd5fe9d69b7561b Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 17 Mar 2011 14:43:00 +0300 Subject: [PATCH] Resolve for 'for' and 'while', 'do..while' pending --- idea/src/jet/lang/Library.jet | 22 +-- .../lang/resolve/ClassDescriptorResolver.java | 23 ++- .../lang/resolve/MutableClassDescriptor.java | 1 + .../jet/lang/types/JetStandardClasses.java | 15 +- .../jet/lang/types/JetStandardLibrary.java | 19 ++ .../jet/lang/types/JetTypeChecker.java | 5 +- .../jet/lang/types/JetTypeInferrer.java | 174 +++++++++++++----- idea/testData/resolve/Basic.jet | 15 ++ .../jet/types/JetTypeCheckerTest.java | 3 +- 9 files changed, 206 insertions(+), 71 deletions(-) diff --git a/idea/src/jet/lang/Library.jet b/idea/src/jet/lang/Library.jet index e79780810b4..6ba209c0c90 100644 --- a/idea/src/jet/lang/Library.jet +++ b/idea/src/jet/lang/Library.jet @@ -159,8 +159,8 @@ class Float : Number, Comparable { fun rangeTo(other : Double) : Range fun rangeTo(other : Float) : Range - fun rangeTo(other : Long) : Range - fun rangeTo(other : Int) : Range + fun rangeTo(other : Long) : Range + fun rangeTo(other : Int) : Range fun rangeTo(other : Short) : Range fun rangeTo(other : Byte) : Range fun rangeTo(other : Char) : Range @@ -221,7 +221,7 @@ class Long : Number, Comparable { fun mod(other : Char) : Long fun rangeTo(other : Double) : Range - fun rangeTo(other : Float) : Range + fun rangeTo(other : Float) : Range fun rangeTo(other : Long) : IntRange fun rangeTo(other : Int) : IntRange fun rangeTo(other : Short) : IntRange @@ -284,7 +284,7 @@ class Int : Number, Comparable { fun mod(other : Char) : Int fun rangeTo(other : Double) : Range - fun rangeTo(other : Float) : Range + fun rangeTo(other : Float) : Range fun rangeTo(other : Long) : IntRange fun rangeTo(other : Int) : IntRange fun rangeTo(other : Short) : IntRange @@ -350,9 +350,9 @@ class Char : Number, Comparable { fun rangeTo(other : Float) : Range fun rangeTo(other : Long) : IntRange fun rangeTo(other : Int) : IntRange - fun rangeTo(other : Short) : IntRange - fun rangeTo(other : Byte) : IntRange - fun rangeTo(other : Char) : IntRange + fun rangeTo(other : Short) : IntRange + fun rangeTo(other : Byte) : IntRange + fun rangeTo(other : Char) : IntRange fun inc() : Char fun dec() : Char @@ -413,8 +413,8 @@ class Short : Number, Comparable { fun rangeTo(other : Float) : Range fun rangeTo(other : Long) : IntRange fun rangeTo(other : Int) : IntRange - fun rangeTo(other : Short) : IntRange - fun rangeTo(other : Byte) : IntRange + fun rangeTo(other : Short) : IntRange + fun rangeTo(other : Byte) : IntRange fun rangeTo(other : Char) : IntRange fun inc() : Short @@ -476,8 +476,8 @@ class Byte : Number, Comparable { fun rangeTo(other : Float) : Range fun rangeTo(other : Long) : IntRange fun rangeTo(other : Int) : IntRange - fun rangeTo(other : Short) : IntRange - fun rangeTo(other : Byte) : IntRange + fun rangeTo(other : Short) : IntRange + fun rangeTo(other : Byte) : IntRange fun rangeTo(other : Char) : IntRange fun inc() : Byte diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index f70dbda8d71..67dfc5f3df8 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -7,7 +7,10 @@ import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.lexer.JetTokens; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; /** * @author abreslav @@ -57,6 +60,7 @@ public class ClassDescriptorResolver { WritableScope parameterScope = descriptor.getUnsubstitutedMemberScope(); + // This call has side-effects on the parameterScope (fills it in) List typeParameters = resolveTypeParameters(descriptor, parameterScope, classElement.getTypeParameters()); @@ -86,6 +90,7 @@ public class ClassDescriptorResolver { // TODO : importing may be a bad idea for (JetType supertype : superclasses) { + assert supertype != null : classElement.getName(); parameterScope.importScope(supertype.getMemberScope()); } @@ -224,7 +229,7 @@ public class ClassDescriptorResolver { typeParameter.getVariance(), typeParameter.getName(), extendsBound == null - ? Collections.singleton(JetStandardClasses.getAnyType()) + ? Collections.singleton(JetStandardClasses.getDefaultBound()) : Collections.singleton(typeResolver.resolveType(extensibleScope, extendsBound)) ); extensibleScope.addTypeParameterDescriptor(typeParameterDescriptor); @@ -250,11 +255,23 @@ public class ClassDescriptorResolver { @NotNull public PropertyDescriptor resolvePropertyDescriptor(@NotNull DeclarationDescriptor containingDeclaration, @NotNull JetScope scope, @NotNull JetParameter parameter) { + JetTypeReference typeReference = parameter.getTypeReference(); + JetType type; + if (typeReference != null) { + type = typeResolver.resolveType(scope, typeReference); + } + else { + type = ErrorType.createErrorType("Annotation is absent"); + } + return resolvePropertyDescriptor(containingDeclaration, parameter, type); + } + + public PropertyDescriptor resolvePropertyDescriptor(@NotNull DeclarationDescriptor containingDeclaration, @NotNull JetParameter parameter, @NotNull JetType type) { return new PropertyDescriptorImpl( containingDeclaration, AttributeResolver.INSTANCE.resolveAttributes(parameter.getModifierList()), parameter.getName(), - typeResolver.resolveType(scope, parameter.getTypeReference())); + type); } public PropertyDescriptor resolvePropertyDescriptor(@NotNull DeclarationDescriptor containingDeclaration, @NotNull JetScope scope, JetProperty property) { diff --git a/idea/src/org/jetbrains/jet/lang/resolve/MutableClassDescriptor.java b/idea/src/org/jetbrains/jet/lang/resolve/MutableClassDescriptor.java index 9660325cce7..5e9231c0ab7 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/MutableClassDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/MutableClassDescriptor.java @@ -37,6 +37,7 @@ public class MutableClassDescriptor extends MutableDeclarationDescriptor impleme return new SubstitutingScope(unsubstitutedMemberScope, substitutionContext); } + @NotNull public WritableScope getUnsubstitutedMemberScope() { return unsubstitutedMemberScope; } diff --git a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java index ff4fac927e6..71883f38094 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java @@ -45,8 +45,8 @@ public class JetStandardClasses { } }, JetScope.EMPTY ); - private static final JetType NOTHING_TYPE = new JetTypeImpl(getNothing()); + private static final JetType NOTHING_TYPE = new JetTypeImpl(getNothing()); private static final JetType NULLABLE_NOTHING_TYPE = new JetTypeImpl( Collections.emptyList(), getNothing().getTypeConstructor(), @@ -65,8 +65,8 @@ public class JetStandardClasses { Collections.emptySet(), JetScope.EMPTY ); - private static final JetType ANY_TYPE = new JetTypeImpl(ANY.getTypeConstructor(), JetScope.EMPTY); + private static final JetType ANY_TYPE = new JetTypeImpl(ANY.getTypeConstructor(), JetScope.EMPTY); private static final JetType NULLABLE_ANY_TYPE = TypeUtils.makeNullable(ANY_TYPE); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -76,8 +76,8 @@ public class JetStandardClasses { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final int TUPLE_COUNT = 22; - private static final ClassDescriptor[] TUPLE = new ClassDescriptor[TUPLE_COUNT]; + private static final ClassDescriptor[] TUPLE = new ClassDescriptor[TUPLE_COUNT]; static { for (int i = 0; i < TUPLE_COUNT; i++) { List parameters = new ArrayList(); @@ -102,8 +102,8 @@ public class JetStandardClasses { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final int FUNCTION_COUNT = 22; - private static final ClassDescriptor[] FUNCTION = new ClassDescriptor[FUNCTION_COUNT]; + private static final ClassDescriptor[] FUNCTION = new ClassDescriptor[FUNCTION_COUNT]; private static final ClassDescriptor[] RECEIVER_FUNCTION = new ClassDescriptor[FUNCTION_COUNT]; static { @@ -191,8 +191,15 @@ public class JetStandardClasses { } } + private static final JetType DEFAULT_BOUND = getNullableAnyType(); + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + @NotNull + public static JetType getDefaultBound() { + return DEFAULT_BOUND; + } + @NotNull public static ClassDescriptor getAny() { return ANY; diff --git a/idea/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java b/idea/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java index a5a732bea49..b95fd2b6502 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java @@ -49,6 +49,7 @@ public class JetStandardLibrary { private final ClassDescriptor booleanClass; private final ClassDescriptor stringClass; private final ClassDescriptor arrayClass; + private final ClassDescriptor iterableClass; private final JetType byteType; private final JetType charType; @@ -86,6 +87,7 @@ public class JetStandardLibrary { this.booleanClass = libraryScope.getClass("Boolean"); this.stringClass = libraryScope.getClass("String"); this.arrayClass = libraryScope.getClass("Array"); + this.iterableClass = libraryScope.getClass("Iterable"); this.byteType = new JetTypeImpl(getByte()); this.charType = new JetTypeImpl(getChar()); @@ -155,6 +157,11 @@ public class JetStandardLibrary { return arrayClass; } + @NotNull + public ClassDescriptor getIterable() { + return iterableClass; + } + @NotNull public JetType getIntType() { return intType; @@ -217,4 +224,16 @@ public class JetStandardLibrary { getArray().getMemberScope(types) ); } + + @NotNull + public JetType getIterableType(@NotNull JetType argument) { + List types = Collections.singletonList(new TypeProjection(Variance.INVARIANT, argument)); + return new JetTypeImpl( + Collections.emptyList(), + getIterable().getTypeConstructor(), + false, + types, + getIterable().getMemberScope(types) + ); + } } diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index 49b80311b59..b46abb64ea2 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -177,11 +177,14 @@ public class JetTypeChecker { } if (ins != null) { - Variance projectionKind = variance == Variance.IN_VARIANCE ? Variance.INVARIANT : Variance.IN_VARIANCE; JetType intersection = TypeUtils.intersect(this, ins); if (intersection == null) { + if (outs != null) { + return new TypeProjection(Variance.OUT_VARIANCE, commonSupertype(outs)); + } return new TypeProjection(Variance.OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds())); } + Variance projectionKind = variance == Variance.IN_VARIANCE ? Variance.INVARIANT : Variance.IN_VARIANCE; return new TypeProjection(projectionKind, intersection); } else if (outs != null) { Variance projectionKind = variance == Variance.OUT_VARIANCE ? Variance.INVARIANT : Variance.OUT_VARIANCE; diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 2a92bed8cee..e3026027335 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -165,7 +165,7 @@ public class JetTypeInferrer { @Override public void visitExpression(JetExpression expression) { // create a dummy domain for the type of e - throw new UnsupportedOperationException(); // TODO + throw new UnsupportedOperationException(expression.getText()); // TODO } @Override @@ -436,55 +436,6 @@ public class JetTypeInferrer { } } - @Override - public void visitIfExpression(JetIfExpression expression) { - // TODO : check condition type - JetExpression condition = expression.getCondition(); - if (condition != null) { - JetType conditionType = getType(scope, condition, false); - - if (conditionType != null && !isBoolean(conditionType)) { - semanticServices.getErrorHandler().genericError(condition.getNode(), "Condition must be of type Boolean, but was of type " + conditionType); - } - } - // TODO : change types according to is and nullability checks - JetExpression elseBranch = expression.getElse(); - if (elseBranch == null) { - // TODO : type-check the branch - result = JetStandardClasses.getUnitType(); - } else { - JetType thenType = getType(scope, expression.getThen(), true); - JetType elseType = getType(scope, elseBranch, true); - result = semanticServices.getTypeChecker().commonSupertype(Arrays.asList(thenType, elseType)); - } - } - - @Override - public void visitWhenExpression(JetWhenExpression expression) { - // TODO :change scope according to the bound value in the when header - List expressions = new ArrayList(); - collectAllReturnTypes(expression, scope, expressions); - result = semanticServices.getTypeChecker().commonSupertype(expressions); - } - - @Override - public void visitTryExpression(JetTryExpression expression) { - JetExpression tryBlock = expression.getTryBlock(); - List catchClauses = expression.getCatchClauses(); - JetFinallySection finallyBlock = expression.getFinallyBlock(); - List types = new ArrayList(); - if (finallyBlock == null) { - for (JetCatchClause catchClause : catchClauses) { - // TODO: change scope here - types.add(getType(scope, catchClause.getCatchBody(), true)); - } - } else { - types.add(getType(scope, finallyBlock.getFinalExpression(), true)); - } - types.add(getType(scope, tryBlock, true)); - result = semanticServices.getTypeChecker().commonSupertype(types); - } - @Override public void visitTupleExpression(JetTupleExpression expression) { List entries = expression.getEntries(); @@ -525,7 +476,125 @@ public class JetTypeInferrer { } @Override - public void visitLoopExpression(JetLoopExpression expression) { + public void visitWhenExpression(JetWhenExpression expression) { + // TODO :change scope according to the bound value in the when header + List expressions = new ArrayList(); + collectAllReturnTypes(expression, scope, expressions); + result = semanticServices.getTypeChecker().commonSupertype(expressions); + } + + @Override + public void visitTryExpression(JetTryExpression expression) { + JetExpression tryBlock = expression.getTryBlock(); + List catchClauses = expression.getCatchClauses(); + JetFinallySection finallyBlock = expression.getFinallyBlock(); + List types = new ArrayList(); + if (finallyBlock == null) { + for (JetCatchClause catchClause : catchClauses) { + // TODO: change scope here + types.add(getType(scope, catchClause.getCatchBody(), true)); + } + } else { + types.add(getType(scope, finallyBlock.getFinalExpression(), true)); + } + types.add(getType(scope, tryBlock, true)); + result = semanticServices.getTypeChecker().commonSupertype(types); + } + + @Override + public void visitIfExpression(JetIfExpression expression) { + checkCondition(scope, expression.getCondition()); + + // TODO : change types according to is and nullability checks + JetExpression elseBranch = expression.getElse(); + if (elseBranch == null) { + // TODO : type-check the branch + result = JetStandardClasses.getUnitType(); + } else { + JetType thenType = getType(scope, expression.getThen(), true); + JetType elseType = getType(scope, elseBranch, true); + result = semanticServices.getTypeChecker().commonSupertype(Arrays.asList(thenType, elseType)); + } + } + + private void checkCondition(@NotNull JetScope scope, @Nullable JetExpression condition) { + if (condition != null) { + JetType conditionType = getType(scope, condition, false); + + if (conditionType != null && !isBoolean(conditionType)) { + semanticServices.getErrorHandler().genericError(condition.getNode(), "Condition must be of type Boolean, but was of type " + conditionType); + } + } + } + + @Override + public void visitWhileExpression(JetWhileExpression expression) { + checkCondition(scope, expression.getCondition()); + JetExpression body = expression.getBody(); + if (body != null) { + getType(scope, body, true); + } + result = JetStandardClasses.getUnitType(); + } + + @Override + public void visitDoWhileExpression(JetDoWhileExpression expression) { + JetExpression body = expression.getBody(); + JetScope conditionScope = scope; // TODO + if (body != null) { + getType(scope, body, true); + } + checkCondition(conditionScope, expression.getCondition()); + result = JetStandardClasses.getUnitType(); + } + + @Override + public void visitForExpression(JetForExpression expression) { + JetParameter loopParameter = expression.getLoopParameter(); + JetExpression loopRange = expression.getLoopRange(); + JetType loopRangeType = getType(scope, loopRange, false); + JetType expectedParameterType = null; + if (loopRangeType != null) { + if (!semanticServices.getTypeChecker().isSubtypeOf(loopRangeType, semanticServices.getStandardLibrary().getIterableType(JetStandardClasses.getNullableAnyType()))) { + semanticServices.getErrorHandler().genericError(loopRange.getNode(), "Expecting an Iterable, but found " + loopRangeType); + } + else { + TypeProjection typeProjection = loopRangeType.getArguments().get(0); + if (!typeProjection.getProjectionKind().allowsOutPosition()) { + expectedParameterType = JetStandardClasses.getDefaultBound(); + } + else { + expectedParameterType = typeProjection.getType(); + } + } + } + + WritableScope loopScope = semanticServices.createWritableScope(scope, scope.getContainingDeclaration()); + + JetTypeReference typeReference = loopParameter.getTypeReference(); + PropertyDescriptor propertyDescriptor; + if (typeReference != null) { + propertyDescriptor = semanticServices.getClassDescriptorResolver(trace).resolvePropertyDescriptor(scope.getContainingDeclaration(), scope, loopParameter); + JetType actualParameterType = propertyDescriptor.getType(); + if (expectedParameterType != null && + !semanticServices.getTypeChecker().isSubtypeOf(expectedParameterType, actualParameterType)) { + semanticServices.getErrorHandler().genericError(typeReference.getNode(), "The loop iterates over values of type " + expectedParameterType + " but the parameter is declared to be " + actualParameterType); + } + } + else { + if (expectedParameterType == null) { + expectedParameterType = ErrorType.createErrorType("Error"); + } + propertyDescriptor = semanticServices.getClassDescriptorResolver(trace).resolvePropertyDescriptor(scope.getContainingDeclaration(), loopParameter, expectedParameterType); + } + loopScope.addPropertyDescriptor(propertyDescriptor); + trace.recordDeclarationResolution(loopParameter, propertyDescriptor); + + JetExpression body = expression.getBody(); + if (body != null) { + getType(loopScope, body, true); // TODO + } + result = JetStandardClasses.getUnitType(); } @@ -844,6 +913,9 @@ public class JetTypeInferrer { private JetType getTypeForBinaryCall(JetScope scope, JetExpression left, JetSimpleNameExpression operationSign, @NotNull JetExpression right, String name, boolean reportUnresolved) { JetType leftType = safeGetType(scope, left, false); JetType rightType = safeGetType(scope, right, false); + if (ErrorType.isErrorType(leftType)) { + return null; + } FunctionDescriptor functionDescriptor = lookupFunction(scope, operationSign, name, leftType, Collections.singletonList(rightType), reportUnresolved); if (functionDescriptor != null) { return functionDescriptor.getUnsubstitutedReturnType(); diff --git a/idea/testData/resolve/Basic.jet b/idea/testData/resolve/Basic.jet index 7a829c67343..5f8989de974 100644 --- a/idea/testData/resolve/Basic.jet +++ b/idea/testData/resolve/Basic.jet @@ -17,3 +17,18 @@ val x : `C.B`B } + +fun test() : Unit { + for (~i~i in 1..2) { + `i`i`:std::Int` + } + for (val ~j~j in 1..2) { + `j`j`:std::Int` + } + for (val ~k~k : Int in 1..2) { + `k`k`:std::Int` + } + for (~l~l : Int in 1..2) { + `l`l`:std::Int` + } +} diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index 0dfaef02168..cd64021c93f 100644 --- a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -141,7 +141,8 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { assertCommonSupertype("Derived_T", "DDerived_T", "Derived_T"); assertCommonSupertype("Derived_T", "DDerived_T", "DDerived1_T"); - assertCommonSupertype("Base_T", "Base_T", "Base_T"); + assertCommonSupertype("Comparable<*>", "Comparable", "Comparable"); + assertCommonSupertype("Base_T>", "Base_T", "Base_T"); assertCommonSupertype("Base_T", "Base_T", "Base_T"); assertCommonSupertype("Base_T", "Derived_T", "Base_T"); assertCommonSupertype("Base_T", "Derived_T", "Base_T");