diff --git a/.idea/dictionaries/abreslav.xml b/.idea/dictionaries/abreslav.xml
index 265db740950..54c8d114ec3 100644
--- a/.idea/dictionaries/abreslav.xml
+++ b/.idea/dictionaries/abreslav.xml
@@ -4,6 +4,7 @@
accessor
dominator
inferrer
+ iterable
nondeterministic
nullable
pseudocode
diff --git a/idea/src/jet/lang/Library.jet b/idea/src/jet/lang/Library.jet
index 6a8b3d0224b..49e5ab1bd64 100644
--- a/idea/src/jet/lang/Library.jet
+++ b/idea/src/jet/lang/Library.jet
@@ -9,9 +9,11 @@ class Iterable {
fun iterator() : Iterator
}
-class Array : Iterable {
+class Array {
fun get(index : Int) : T
fun set(index : Int, value : T) : Unit
+
+ fun iterator() : Iterator
}
virtual class Comparable {
diff --git a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java
index fac01ff688b..3de69c9bfa1 100644
--- a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java
+++ b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java
@@ -188,7 +188,8 @@ public class JetControlFlowProcessor {
JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left;
visitAssignToArrayAccess(expression, arrayAccessExpression);
} else if (left instanceof JetQualifiedExpression) {
- //
+ assert !(left instanceof JetPredicateExpression) : left; // TODO
+ assert !(left instanceof JetHashQualifiedExpression) : left; // TODO
JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) left;
value(qualifiedExpression.getReceiverExpression(), false, false);
value(expression.getOperationReference(), false, false);
@@ -375,7 +376,10 @@ public class JetControlFlowProcessor {
@Override
public void visitForExpression(JetForExpression expression) {
- value(expression.getLoopRange(), false, false);
+ JetExpression loopRange = expression.getLoopRange();
+ if (loopRange != null) {
+ value(loopRange, false, false);
+ }
// TODO : primitive cases
Label loopExitPoint = builder.createUnboundLabel();
builder.nondeterministicJump(loopExitPoint);
diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetForExpression.java b/idea/src/org/jetbrains/jet/lang/psi/JetForExpression.java
index 423ad6c619a..e6a6585fe2c 100644
--- a/idea/src/org/jetbrains/jet/lang/psi/JetForExpression.java
+++ b/idea/src/org/jetbrains/jet/lang/psi/JetForExpression.java
@@ -2,6 +2,7 @@ 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;
/**
@@ -17,10 +18,12 @@ public class JetForExpression extends JetLoopExpression {
visitor.visitForExpression(this);
}
+ @Nullable @IfNotParsed
public JetParameter getLoopParameter() {
return (JetParameter) findChildByType(JetNodeTypes.LOOP_PARAMETER);
}
+ @Nullable @IfNotParsed
public JetExpression getLoopRange() {
return findExpressionUnder(JetNodeTypes.LOOP_RANGE);
}
diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java
index 849b8aeafd8..081a142c2c8 100644
--- a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java
+++ b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java
@@ -70,7 +70,7 @@ public class ClassDescriptorResolver {
}
public void resolveMutableClassDescriptor(@NotNull JetScope scope, @NotNull JetClass classElement, @NotNull MutableClassDescriptor descriptor) {
- descriptor.setName(classElement.getName());
+ descriptor.setName(JetPsiUtil.safeName(classElement.getName()));
descriptor.getClassHeaderScope().addLabeledDeclaration(descriptor);
WritableScope parameterScope = descriptor.getClassHeaderScope();
diff --git a/idea/src/org/jetbrains/jet/lang/resolve/MutableDeclarationDescriptor.java b/idea/src/org/jetbrains/jet/lang/resolve/MutableDeclarationDescriptor.java
index 76b5ac43e50..6c0440ebf10 100644
--- a/idea/src/org/jetbrains/jet/lang/resolve/MutableDeclarationDescriptor.java
+++ b/idea/src/org/jetbrains/jet/lang/resolve/MutableDeclarationDescriptor.java
@@ -29,7 +29,7 @@ public abstract class MutableDeclarationDescriptor implements DeclarationDescrip
return name;
}
- public void setName(String name) {
+ public void setName(@NotNull String name) {
this.name = name;
}
diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java
index 0b6e70069fa..e58a2cbd663 100644
--- a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java
+++ b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java
@@ -124,7 +124,7 @@ public class TopDownAnalyzer {
private WritableScope processClass(@NotNull WritableScope declaringScope, JetClass klass) {
MutableClassDescriptor mutableClassDescriptor = new MutableClassDescriptor(semanticServices, declaringScope.getContainingDeclaration(), declaringScope);
- mutableClassDescriptor.setName(klass.getName());
+ mutableClassDescriptor.setName(JetPsiUtil.safeName(klass.getName()));
declaringScope.addClassifierDescriptor(mutableClassDescriptor);
diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java
index 387eb004aad..9c2d75f1d61 100644
--- a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java
+++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java
@@ -75,6 +75,7 @@ public class JetTypeChecker {
boolean nullable = false;
for (Iterator iterator = typeSet.iterator(); iterator.hasNext();) {
JetType type = iterator.next();
+ // TODO : This admits 'Nothing?'. Review
if (JetStandardClasses.isNothing(type)) {
iterator.remove();
}
diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java
index 5be9feb485d..4e69be720ba 100644
--- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java
+++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java
@@ -1,6 +1,7 @@
package org.jetbrains.jet.lang.types;
import com.google.common.collect.ImmutableMap;
+import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
@@ -834,43 +835,37 @@ public class JetTypeInferrer {
public void visitForExpression(JetForExpression expression) {
JetParameter loopParameter = expression.getLoopParameter();
JetExpression loopRange = expression.getLoopRange();
- JetType loopRangeType = getType(scope, loopRange, false);
+ JetType loopRangeType = null;
+ if (loopRange != null) {
+ 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();
- }
- }
+ expectedParameterType = checkIterableConvention(loopRangeType, loopRange.getNode());
}
WritableScope loopScope = semanticServices.createWritableScope(scope, scope.getContainingDeclaration());
- JetTypeReference typeReference = loopParameter.getTypeReference();
- PropertyDescriptor propertyDescriptor;
- if (typeReference != null) {
- propertyDescriptor = classDescriptorResolver.resolveValueParameterDescriptor(scope.getContainingDeclaration(), scope, loopParameter);
- JetType actualParameterType = propertyDescriptor.getOutType();
- if (expectedParameterType != null &&
- actualParameterType != 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);
+ if (loopParameter != null) {
+ JetTypeReference typeReference = loopParameter.getTypeReference();
+ PropertyDescriptor propertyDescriptor;
+ if (typeReference != null) {
+ propertyDescriptor = classDescriptorResolver.resolveValueParameterDescriptor(scope.getContainingDeclaration(), scope, loopParameter);
+ JetType actualParameterType = propertyDescriptor.getOutType();
+ if (expectedParameterType != null &&
+ actualParameterType != 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 = ErrorUtils.createErrorType("Error");
+ else {
+ if (expectedParameterType == null) {
+ expectedParameterType = ErrorUtils.createErrorType("Error");
+ }
+ propertyDescriptor = classDescriptorResolver.resolveValueParameterDescriptor(scope.getContainingDeclaration(), loopParameter, expectedParameterType);
}
- propertyDescriptor = classDescriptorResolver.resolveValueParameterDescriptor(scope.getContainingDeclaration(), loopParameter, expectedParameterType);
+ loopScope.addPropertyDescriptor(propertyDescriptor);
}
- loopScope.addPropertyDescriptor(propertyDescriptor);
JetExpression body = expression.getBody();
if (body != null) {
@@ -880,6 +875,79 @@ public class JetTypeInferrer {
result = JetStandardClasses.getUnitType();
}
+ @Nullable
+ private JetType checkIterableConvention(@NotNull JetType type, @NotNull ASTNode reportErrorsOn) {
+ OverloadResolutionResult iteratorResolutionResult = resolveNoParametersFunction(type, scope, "iterator");
+ if (iteratorResolutionResult.isSuccess()) {
+ JetType iteratorType = iteratorResolutionResult.getFunctionDescriptor().getUnsubstitutedReturnType();
+ boolean hasNextFunctionSupported = checkHasNextFunctionSupport(reportErrorsOn, iteratorType);
+ boolean hasNextPropertySupported = checkHasNextPropertySupport(reportErrorsOn, iteratorType);
+ if (hasNextFunctionSupported && hasNextPropertySupported) {
+ // TODO : overload resolution rules impose priorities here???
+ semanticServices.getErrorHandler().genericError(reportErrorsOn, "An ambiguity between 'iterator().hasNext()' function and 'iterator().hasNext()' property");
+ }
+ else if (!hasNextFunctionSupported && !hasNextPropertySupported) {
+ semanticServices.getErrorHandler().genericError(reportErrorsOn, "Loop range must have an 'iterator().hasNext()' function or an 'iterator().hasNext' property");
+ }
+
+ OverloadResolutionResult nextResolutionResult = resolveNoParametersFunction(iteratorType, scope, "next");
+ if (nextResolutionResult.isAmbiguity()) {
+ semanticServices.getErrorHandler().genericError(reportErrorsOn, "Method 'iterator().next()' is ambiguous for this expression");
+ } else if (nextResolutionResult.isNothing()) {
+ semanticServices.getErrorHandler().genericError(reportErrorsOn, "Loop range must have an 'iterator().next()' method");
+ } else {
+ return nextResolutionResult.getFunctionDescriptor().getUnsubstitutedReturnType();
+ }
+ }
+ else {
+ String errorMessage = "For-loop range must have an iterator() method";
+ if (iteratorResolutionResult.isAmbiguity()) {
+ errorMessage = "Method 'iterator()' is ambiguous for this expression";
+ }
+ semanticServices.getErrorHandler().genericError(reportErrorsOn, errorMessage);
+ }
+ return null;
+ }
+
+ private boolean checkHasNextFunctionSupport(@NotNull ASTNode reportErrorsOn, @NotNull JetType iteratorType) {
+ OverloadResolutionResult hasNextResolutionResult = resolveNoParametersFunction(iteratorType, scope, "hasNext");
+ if (hasNextResolutionResult.isAmbiguity()) {
+ semanticServices.getErrorHandler().genericError(reportErrorsOn, "Method 'iterator().hasNext()' is ambiguous for this expression");
+ } else if (hasNextResolutionResult.isNothing()) {
+ return false;
+ } else {
+ JetType hasNextReturnType = hasNextResolutionResult.getFunctionDescriptor().getUnsubstitutedReturnType();
+ if (!isBoolean(hasNextReturnType)) {
+ semanticServices.getErrorHandler().genericError(reportErrorsOn, "The 'iterator().hasNext()' method of the loop range must return Boolean, but returns " + hasNextReturnType);
+ }
+ }
+ return true;
+ }
+
+ private boolean checkHasNextPropertySupport(@NotNull ASTNode reportErrorsOn, @NotNull JetType iteratorType) {
+ PropertyDescriptor hasNextProperty = iteratorType.getMemberScope().getProperty("hasNext");
+ // TODO :extension properties
+ if (hasNextProperty == null) {
+ return false;
+ } else {
+ JetType hasNextReturnType = hasNextProperty.getOutType();
+ if (hasNextReturnType == null) {
+ // TODO : accessibility
+ semanticServices.getErrorHandler().genericError(reportErrorsOn, "The 'iterator().hasNext' property of the loop range must be readable");
+ }
+ else if (!isBoolean(hasNextReturnType)) {
+ semanticServices.getErrorHandler().genericError(reportErrorsOn, "The 'iterator().hasNext' property of the loop range must return Boolean, but returns " + hasNextReturnType);
+ }
+ }
+ return true;
+ }
+
+ @NotNull
+ private OverloadResolutionResult resolveNoParametersFunction(@NotNull JetType receiverType, @NotNull JetScope scope, @NotNull String name) {
+ OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name);
+ return overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.emptyList(), Collections.emptyList());
+ }
+
@Override
public void visitNewExpression(JetNewExpression expression) {
// TODO : type argument inference
diff --git a/idea/testData/checker/ForRangeConventions.jet b/idea/testData/checker/ForRangeConventions.jet
new file mode 100644
index 00000000000..86e66214c61
--- /dev/null
+++ b/idea/testData/checker/ForRangeConventions.jet
@@ -0,0 +1,76 @@
+class NotRange1 {
+
+}
+
+class NotRange2 {
+ fun iterator() : Unit
+}
+
+class ImproperIterator1 {
+ fun hasNext() : Boolean
+}
+
+class NotRange3 {
+ fun iterator() : ImproperIterator1
+}
+
+class ImproperIterator2 {
+ fun next() : Boolean
+}
+
+class NotRange4 {
+ fun iterator() : ImproperIterator2
+}
+
+class ImproperIterator3 {
+ fun hasNext() : Int
+ fun next() : Int
+}
+
+class NotRange5 {
+ fun iterator() : ImproperIterator3
+}
+
+class AmbiguousHasNextIterator {
+ fun hasNext() : Boolean
+ val hasNext : Boolean
+ fun next() : Int
+}
+
+class NotRange6 {
+ fun iterator() : AmbiguousHasNextIterator
+}
+
+class ImproperIterator4 {
+ val hasNext : Int
+ fun next() : Int
+}
+
+class NotRange7 {
+ fun iterator() : ImproperIterator3
+}
+
+class GoodIterator {
+ fun hasNext() : Boolean
+ fun next() : Int
+}
+
+class Range0 {
+ fun iterator() : GoodIterator
+}
+
+class Range1 {
+ fun iterator() : Iterator
+}
+
+fun test() {
+ for (i in new NotRange1());
+ for (i in new NotRange2());
+ for (i in new NotRange3());
+ for (i in new NotRange4());
+ for (i in new NotRange5());
+ for (i in new NotRange6());
+ for (i in new NotRange7());
+ for (i in new Range0());
+ for (i in new Range1());
+}