Sophisticated conventions for 'for'-loop ranges
This commit is contained in:
Generated
+1
@@ -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>
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
Reference in New Issue
Block a user