Sophisticated conventions for 'for'-loop ranges

This commit is contained in:
Andrey Breslav
2011-04-15 21:23:01 +04:00
parent 2311426ba6
commit 319a250349
10 changed files with 189 additions and 34 deletions
+1
View File
@@ -4,6 +4,7 @@
<w>accessor</w>
<w>dominator</w>
<w>inferrer</w>
<w>iterable</w>
<w>nondeterministic</w>
<w>nullable</w>
<w>pseudocode</w>
+3 -1
View File
@@ -9,9 +9,11 @@ class Iterable<out T> {
fun iterator() : Iterator<T>
}
class Array<T> : Iterable<T> {
class Array<T> {
fun get(index : Int) : T
fun set(index : Int, value : T) : Unit
fun iterator() : Iterator<T>
}
virtual class Comparable<in T> {
@@ -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);
@@ -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);
}
@@ -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();
@@ -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;
}
@@ -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);
@@ -75,6 +75,7 @@ public class JetTypeChecker {
boolean nullable = false;
for (Iterator<JetType> iterator = typeSet.iterator(); iterator.hasNext();) {
JetType type = iterator.next();
// TODO : This admits 'Nothing?'. Review
if (JetStandardClasses.isNothing(type)) {
iterator.remove();
}
@@ -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.<JetType>emptyList(), Collections.<JetType>emptyList());
}
@Override
public void visitNewExpression(JetNewExpression expression) {
// TODO : type argument inference
@@ -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<Int>
}
fun test() {
for (i in <error>new NotRange1()</error>);
for (i in <error>new NotRange2()</error>);
for (i in <error>new NotRange3()</error>);
for (i in <error>new NotRange4()</error>);
for (i in <error>new NotRange5()</error>);
for (i in <error>new NotRange6()</error>);
for (i in <error>new NotRange7()</error>);
for (i in new Range0());
for (i in new Range1());
}