Merge remote branch 'origin/master'

This commit is contained in:
Andrey Breslav
2011-11-28 21:44:16 +03:00
9 changed files with 176 additions and 117 deletions
@@ -25,12 +25,11 @@ public class SemanticWhitespaceAwarePsiBuilderImpl extends PsiBuilderAdapter imp
@Override @Override
public boolean newlineBeforeCurrentToken() { public boolean newlineBeforeCurrentToken() {
if (!newlinesEnabled.peek()) return false; if (!newlinesEnabled.peek()) return false;
if (eof()) return true; if (eof()) return true;
// TODO: maybe, memorize this somehow? // TODO: maybe, memoize this somehow?
for (int i = 1; i <= getCurrentOffset(); i++) { for (int i = 1; i <= getCurrentOffset(); i++) {
IElementType previousToken = rawLookup(-i); IElementType previousToken = rawLookup(-i);
@@ -51,8 +50,9 @@ public class SemanticWhitespaceAwarePsiBuilderImpl extends PsiBuilderAdapter imp
assert previousTokenEnd < getOriginalText().length(); assert previousTokenEnd < getOriginalText().length();
for (int j = previousTokenStart; j < previousTokenEnd; j++) { for (int j = previousTokenStart; j < previousTokenEnd; j++) {
if (getOriginalText().charAt(j) == '\n') if (getOriginalText().charAt(j) == '\n') {
return true; return true;
}
} }
} }
@@ -651,7 +651,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return DataFlowUtils.checkType(result, expression, context); return DataFlowUtils.checkType(result, expression, context);
} }
protected void checkLValue(BindingTrace trace, JetExpression expression) { public void checkLValue(BindingTrace trace, JetExpression expression) {
checkLValue(trace, expression, false); checkLValue(trace, expression, false);
} }
@@ -685,8 +685,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
} }
} }
else if (OperatorConventions.BINARY_OPERATION_NAMES.containsKey(operationType)) { else if (OperatorConventions.BINARY_OPERATION_NAMES.containsKey(operationType)) {
result = getTypeForBinaryCall(context.scope, OperatorConventions.BINARY_OPERATION_NAMES.get(operationType), context, expression); result = getTypeForBinaryCall(context.scope, OperatorConventions.BINARY_OPERATION_NAMES.get(operationType), context, expression);
} }
else if (operationType == JetTokens.EQ) { else if (operationType == JetTokens.EQ) {
result = visitAssignment(expression, contextWithExpectedType); result = visitAssignment(expression, contextWithExpectedType);
} }
@@ -706,74 +706,77 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
} }
} }
} }
else if (OperatorConventions.EQUALS_OPERATIONS.contains(operationType)) { else {
String name = "equals"; JetType booleanType = context.semanticServices.getStandardLibrary().getBooleanType();
if (right != null) { if (OperatorConventions.EQUALS_OPERATIONS.contains(operationType)) {
ExpressionReceiver receiver = ExpressionTypingUtils.safeGetExpressionReceiver(facade, left, context.replaceScope(context.scope)); String name = "equals";
OverloadResolutionResults<FunctionDescriptor> resolutionResults = context.resolveExactSignature( if (right != null) {
receiver, "equals", ExpressionReceiver receiver = ExpressionTypingUtils.safeGetExpressionReceiver(facade, left, context.replaceScope(context.scope));
Collections.singletonList(JetStandardClasses.getNullableAnyType())); OverloadResolutionResults<FunctionDescriptor> resolutionResults = context.resolveExactSignature(
if (resolutionResults.isSuccess()) { receiver, "equals",
FunctionDescriptor equals = resolutionResults.getResult().getResultingDescriptor(); Collections.singletonList(JetStandardClasses.getNullableAnyType()));
context.trace.record(REFERENCE_TARGET, operationSign, equals); if (resolutionResults.isSuccess()) {
if (ensureBooleanResult(operationSign, name, equals.getReturnType(), context)) { FunctionDescriptor equals = resolutionResults.getResult().getResultingDescriptor();
ensureNonemptyIntersectionOfOperandTypes(expression, context); context.trace.record(REFERENCE_TARGET, operationSign, equals);
} if (ensureBooleanResult(operationSign, name, equals.getReturnType(), context)) {
} ensureNonemptyIntersectionOfOperandTypes(expression, context);
else { }
if (resolutionResults.isAmbiguity()) {
context.trace.report(OVERLOAD_RESOLUTION_AMBIGUITY.on(operationSign, resolutionResults.getResults()));
} }
else { else {
context.trace.report(EQUALS_MISSING.on(operationSign)); if (resolutionResults.isAmbiguity()) {
context.trace.report(OVERLOAD_RESOLUTION_AMBIGUITY.on(operationSign, resolutionResults.getResults()));
}
else {
context.trace.report(EQUALS_MISSING.on(operationSign));
}
}
}
result = booleanType;
}
else if (operationType == JetTokens.EQEQEQ || operationType == JetTokens.EXCLEQEQEQ) {
ensureNonemptyIntersectionOfOperandTypes(expression, context);
// TODO : Check comparison pointlessness
result = booleanType;
}
else if (OperatorConventions.IN_OPERATIONS.contains(operationType)) {
if (right == null) {
result = ErrorUtils.createErrorType("No right argument"); // TODO
return null;
}
checkInExpression(expression, expression.getOperationReference(), expression.getLeft(), expression.getRight(), context);
result = booleanType;
}
else if (operationType == JetTokens.ANDAND || operationType == JetTokens.OROR) {
JetType leftType = facade.getType(left, context.replaceScope(context.scope));
WritableScopeImpl leftScope = newWritableScopeImpl(context).setDebugName("Left scope of && or ||");
DataFlowInfo flowInfoLeft = DataFlowUtils.extractDataFlowInfoFromCondition(left, operationType == JetTokens.ANDAND, leftScope, context); // TODO: This gets computed twice: here and in extractDataFlowInfoFromCondition() for the whole condition
WritableScopeImpl rightScope = operationType == JetTokens.ANDAND ? leftScope : newWritableScopeImpl(context).setDebugName("Right scope of && or ||");
JetType rightType = right == null ? null : facade.getType(right, context.replaceDataFlowInfo(flowInfoLeft).replaceScope(rightScope));
if (leftType != null && !isBoolean(context.semanticServices, leftType)) {
context.trace.report(TYPE_MISMATCH.on(left, booleanType, leftType));
}
if (rightType != null && !isBoolean(context.semanticServices, rightType)) {
context.trace.report(TYPE_MISMATCH.on(right, booleanType, rightType));
}
result = booleanType;
}
else if (operationType == JetTokens.ELVIS) {
JetType leftType = facade.getType(left, context.replaceScope(context.scope));
JetType rightType = right == null ? null : facade.getType(right, contextWithExpectedType.replaceScope(context.scope));
if (leftType != null) {
if (!leftType.isNullable()) {
context.trace.report(USELESS_ELVIS.on(expression, left, leftType));
}
if (rightType != null) {
DataFlowUtils.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType);
return TypeUtils.makeNullableAsSpecified(CommonSupertypes.commonSupertype(Arrays.asList(leftType, rightType)), rightType.isNullable());
} }
} }
} }
result = context.semanticServices.getStandardLibrary().getBooleanType(); else {
} context.trace.report(UNSUPPORTED.on(operationSign, "Unknown operation"));
else if (operationType == JetTokens.EQEQEQ || operationType == JetTokens.EXCLEQEQEQ) {
ensureNonemptyIntersectionOfOperandTypes(expression, context);
// TODO : Check comparison pointlessness
result = context.semanticServices.getStandardLibrary().getBooleanType();
}
else if (OperatorConventions.IN_OPERATIONS.contains(operationType)) {
if (right == null) {
result = ErrorUtils.createErrorType("No right argument"); // TODO
return null;
} }
checkInExpression(expression, expression.getOperationReference(), expression.getLeft(), expression.getRight(), context);
result = context.semanticServices.getStandardLibrary().getBooleanType();
}
else if (operationType == JetTokens.ANDAND || operationType == JetTokens.OROR) {
JetType leftType = facade.getType(left, context.replaceScope(context.scope));
WritableScopeImpl leftScope = newWritableScopeImpl(context).setDebugName("Left scope of && or ||");
DataFlowInfo flowInfoLeft = DataFlowUtils.extractDataFlowInfoFromCondition(left, operationType == JetTokens.ANDAND, leftScope, context); // TODO: This gets computed twice: here and in extractDataFlowInfoFromCondition() for the whole condition
WritableScopeImpl rightScope = operationType == JetTokens.ANDAND ? leftScope : newWritableScopeImpl(context).setDebugName("Right scope of && or ||");
JetType rightType = right == null ? null : facade.getType(right, context.replaceDataFlowInfo(flowInfoLeft).replaceScope(rightScope));
if (leftType != null && !isBoolean(context.semanticServices, leftType)) {
context.trace.report(TYPE_MISMATCH.on(left, context.semanticServices.getStandardLibrary().getBooleanType(), leftType));
}
if (rightType != null && !isBoolean(context.semanticServices, rightType)) {
context.trace.report(TYPE_MISMATCH.on(right, context.semanticServices.getStandardLibrary().getBooleanType(), rightType));
}
result = context.semanticServices.getStandardLibrary().getBooleanType();
}
else if (operationType == JetTokens.ELVIS) {
JetType leftType = facade.getType(left, context.replaceScope(context.scope));
JetType rightType = right == null ? null : facade.getType(right, contextWithExpectedType.replaceScope(context.scope));
if (leftType != null) {
if (!leftType.isNullable()) {
context.trace.report(USELESS_ELVIS.on(expression, left, leftType));
}
if (rightType != null) {
DataFlowUtils.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType);
return TypeUtils.makeNullableAsSpecified(CommonSupertypes.commonSupertype(Arrays.asList(leftType, rightType)), rightType.isNullable());
}
}
}
else {
context.trace.report(UNSUPPORTED.on(operationSign, "Unknown operation"));
} }
return DataFlowUtils.checkType(result, expression, contextWithExpectedType); return DataFlowUtils.checkType(result, expression, contextWithExpectedType);
} }
@@ -842,7 +845,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
} }
@Nullable @Nullable
protected JetType getTypeForBinaryCall(JetScope scope, String name, ExpressionTypingContext context, JetBinaryExpression binaryExpression) { public JetType getTypeForBinaryCall(JetScope scope, String name, ExpressionTypingContext context, JetBinaryExpression binaryExpression) {
ExpressionReceiver receiver = safeGetExpressionReceiver(facade, binaryExpression.getLeft(), context.replaceScope(scope)); ExpressionReceiver receiver = safeGetExpressionReceiver(facade, binaryExpression.getLeft(), context.replaceScope(scope));
FunctionDescriptor functionDescriptor = context.replaceScope(scope).resolveCallWithGivenNameToDescriptor( FunctionDescriptor functionDescriptor = context.replaceScope(scope).resolveCallWithGivenNameToDescriptor(
CallMaker.makeCall(receiver, binaryExpression), CallMaker.makeCall(receiver, binaryExpression),
@@ -10,10 +10,11 @@ import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults;
import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.calls.TaskPrioritizers; import org.jetbrains.jet.lang.resolve.calls.TaskPrioritizers;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl; import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
@@ -21,7 +22,10 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver;
import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.lang.types.*;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.jet.lang.diagnostics.Errors.*; import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.resolve.BindingContext.*; import static org.jetbrains.jet.lang.resolve.BindingContext.*;
@@ -36,10 +40,6 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
super(facade); super(facade);
} }
private JetType getTypeWithNewScopeAndDataFlowInfo(@NotNull JetScope scope, @NotNull JetExpression expression, @NotNull DataFlowInfo newDataFlowInfo, @NotNull ExpressionTypingContext context) {
return facade.getType(expression, context.replaceScope(scope).replaceDataFlowInfo(newDataFlowInfo));
}
private void checkCondition(@NotNull JetScope scope, @Nullable JetExpression condition, ExpressionTypingContext context) { private void checkCondition(@NotNull JetScope scope, @Nullable JetExpression condition, ExpressionTypingContext context) {
if (condition != null) { if (condition != null) {
JetType conditionType = facade.getType(condition, context.replaceScope(scope)); JetType conditionType = facade.getType(condition, context.replaceScope(scope));
@@ -63,30 +63,29 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
JetExpression thenBranch = expression.getThen(); JetExpression thenBranch = expression.getThen();
WritableScopeImpl thenScope = newWritableScopeImpl(context).setDebugName("Then scope"); WritableScopeImpl thenScope = newWritableScopeImpl(context).setDebugName("Then scope");
WritableScopeImpl elseScope = newWritableScopeImpl(context).setDebugName("Else scope");
DataFlowInfo thenInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, true, thenScope, context); DataFlowInfo thenInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, true, thenScope, context);
DataFlowInfo elseInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, null, context); DataFlowInfo elseInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, null, context);
if (elseBranch == null) { if (elseBranch == null) {
if (thenBranch != null) { if (thenBranch != null) {
JetType type = getTypeWithNewScopeAndDataFlowInfo(thenScope, thenBranch, thenInfo, context); JetType type = context.getServices().getBlockReturnedTypeWithWritableScope(thenScope, Collections.singletonList(thenBranch), CoercionStrategy.NO_COERCION, context.replaceDataFlowInfo(thenInfo));
if (type != null && JetStandardClasses.isNothing(type)) { if (type != null && JetStandardClasses.isNothing(type)) {
facade.setResultingDataFlowInfo(elseInfo); facade.setResultingDataFlowInfo(elseInfo);
// resultScope = elseScope;
} }
return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType); return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
} }
return null; return null;
} }
if (thenBranch == null) { if (thenBranch == null) {
JetType type = getTypeWithNewScopeAndDataFlowInfo(context.scope, elseBranch, elseInfo, context); JetType type = context.getServices().getBlockReturnedTypeWithWritableScope(elseScope, Collections.singletonList(elseBranch), CoercionStrategy.NO_COERCION, context.replaceDataFlowInfo(elseInfo));
if (type != null && JetStandardClasses.isNothing(type)) { if (type != null && JetStandardClasses.isNothing(type)) {
facade.setResultingDataFlowInfo(thenInfo); facade.setResultingDataFlowInfo(thenInfo);
// resultScope = thenScope;
} }
return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType); return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
} }
JetType thenType = getTypeWithNewScopeAndDataFlowInfo(thenScope, thenBranch, thenInfo, contextWithExpectedType); JetType thenType = context.getServices().getBlockReturnedTypeWithWritableScope(thenScope, Collections.singletonList(thenBranch), CoercionStrategy.NO_COERCION, contextWithExpectedType.replaceDataFlowInfo(thenInfo));
JetType elseType = getTypeWithNewScopeAndDataFlowInfo(context.scope, elseBranch, elseInfo, contextWithExpectedType); JetType elseType = context.getServices().getBlockReturnedTypeWithWritableScope(elseScope, Collections.singletonList(elseBranch), CoercionStrategy.NO_COERCION, contextWithExpectedType.replaceDataFlowInfo(elseInfo));
JetType result; JetType result;
if (thenType == null) { if (thenType == null) {
@@ -120,7 +119,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
if (body != null) { if (body != null) {
WritableScopeImpl scopeToExtend = newWritableScopeImpl(context).setDebugName("Scope extended in while's condition"); WritableScopeImpl scopeToExtend = newWritableScopeImpl(context).setDebugName("Scope extended in while's condition");
DataFlowInfo conditionInfo = condition == null ? context.dataFlowInfo : DataFlowUtils.extractDataFlowInfoFromCondition(condition, true, scopeToExtend, context); DataFlowInfo conditionInfo = condition == null ? context.dataFlowInfo : DataFlowUtils.extractDataFlowInfoFromCondition(condition, true, scopeToExtend, context);
getTypeWithNewScopeAndDataFlowInfo(scopeToExtend, body, conditionInfo, context); context.getServices().getBlockReturnedTypeWithWritableScope(scopeToExtend, Collections.singletonList(body), CoercionStrategy.NO_COERCION, context.replaceDataFlowInfo(conditionInfo));
} }
if (!containsBreak(expression, context)) { if (!containsBreak(expression, context)) {
facade.setResultingDataFlowInfo(DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, null, context)); facade.setResultingDataFlowInfo(DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, null, context));
@@ -229,8 +228,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
JetExpression body = expression.getBody(); JetExpression body = expression.getBody();
if (body != null) { if (body != null) {
ExpressionTypingInternals blockLevelVisitor = ExpressionTypingVisitorDispatcher.createForBlock(loopScope); context.getServices().getBlockReturnedTypeWithWritableScope(loopScope, Collections.singletonList(body), CoercionStrategy.NO_COERCION, context);
blockLevelVisitor.getType(body, context.replaceScope(loopScope));
} }
return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType); return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType);
@@ -14,4 +14,7 @@ public interface ExpressionTypingFacade {
@Nullable @Nullable
JetType getType(@NotNull JetExpression expression, ExpressionTypingContext context); JetType getType(@NotNull JetExpression expression, ExpressionTypingContext context);
@Nullable
JetType getTypeForStatement(@NotNull JetExpression expression, ExpressionTypingContext context);
} }
@@ -218,13 +218,13 @@ public class ExpressionTypingServices {
final boolean[] mismatch = new boolean[1]; final boolean[] mismatch = new boolean[1];
ObservableBindingTrace errorInterceptingTrace = makeTraceInterceptingTypeMismatch(temporaryTraceExpectingUnit, statementExpression, mismatch); ObservableBindingTrace errorInterceptingTrace = makeTraceInterceptingTypeMismatch(temporaryTraceExpectingUnit, statementExpression, mismatch);
newContext = createContext(newContext, errorInterceptingTrace, scope, newContext.dataFlowInfo, context.expectedType, context.expectedReturnType); newContext = createContext(newContext, errorInterceptingTrace, scope, newContext.dataFlowInfo, context.expectedType, context.expectedReturnType);
result = blockLevelVisitor.getType(statementExpression, newContext); result = blockLevelVisitor.getTypeForStatement(statementExpression, newContext);
if (mismatch[0]) { if (mismatch[0]) {
TemporaryBindingTrace temporaryTraceNoExpectedType = TemporaryBindingTrace.create(trace); TemporaryBindingTrace temporaryTraceNoExpectedType = TemporaryBindingTrace.create(trace);
mismatch[0] = false; mismatch[0] = false;
ObservableBindingTrace interceptingTrace = makeTraceInterceptingTypeMismatch(temporaryTraceNoExpectedType, statementExpression, mismatch); ObservableBindingTrace interceptingTrace = makeTraceInterceptingTypeMismatch(temporaryTraceNoExpectedType, statementExpression, mismatch);
newContext = createContext(newContext, interceptingTrace, scope, newContext.dataFlowInfo, NO_EXPECTED_TYPE, context.expectedReturnType); newContext = createContext(newContext, interceptingTrace, scope, newContext.dataFlowInfo, NO_EXPECTED_TYPE, context.expectedReturnType);
result = blockLevelVisitor.getType(statementExpression, newContext); result = blockLevelVisitor.getTypeForStatement(statementExpression, newContext);
if (mismatch[0]) { if (mismatch[0]) {
temporaryTraceExpectingUnit.commit(); temporaryTraceExpectingUnit.commit();
} }
@@ -238,11 +238,11 @@ public class ExpressionTypingServices {
} }
else { else {
newContext = createContext(newContext, trace, scope, newContext.dataFlowInfo, context.expectedType, context.expectedReturnType); newContext = createContext(newContext, trace, scope, newContext.dataFlowInfo, context.expectedType, context.expectedReturnType);
result = blockLevelVisitor.getType(statementExpression, newContext); result = blockLevelVisitor.getTypeForStatement(statementExpression, newContext);
} }
} }
else { else {
result = blockLevelVisitor.getType(statementExpression, newContext); result = blockLevelVisitor.getTypeForStatement(statementExpression, newContext);
if (coercionStrategyForLastExpression == CoercionStrategy.COERCION_TO_UNIT) { if (coercionStrategyForLastExpression == CoercionStrategy.COERCION_TO_UNIT) {
boolean mightBeUnit = false; boolean mightBeUnit = false;
if (statementExpression instanceof JetDeclaration) { if (statementExpression instanceof JetDeclaration) {
@@ -264,7 +264,7 @@ public class ExpressionTypingServices {
} }
} }
else { else {
result = blockLevelVisitor.getType(statementExpression, newContext); result = blockLevelVisitor.getTypeForStatement(statementExpression, newContext);
} }
DataFlowInfo newDataFlowInfo = blockLevelVisitor.getResultingDataFlowInfo(); DataFlowInfo newDataFlowInfo = blockLevelVisitor.getResultingDataFlowInfo();
@@ -14,6 +14,7 @@ import org.jetbrains.jet.lang.types.ErrorUtils;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.util.lazy.ReenteringLazyValueComputationException; import org.jetbrains.jet.util.lazy.ReenteringLazyValueComputationException;
import static org.jetbrains.jet.lang.diagnostics.Errors.ELSE_MISPLACED_IN_WHEN;
import static org.jetbrains.jet.lang.diagnostics.Errors.TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM; import static org.jetbrains.jet.lang.diagnostics.Errors.TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM;
/** /**
@@ -21,36 +22,33 @@ import static org.jetbrains.jet.lang.diagnostics.Errors.TYPECHECKER_HAS_RUN_INTO
*/ */
public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetType, ExpressionTypingContext> implements ExpressionTypingInternals { public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetType, ExpressionTypingContext> implements ExpressionTypingInternals {
private static Function<ExpressionTypingInternals, BasicExpressionTypingVisitor> BASIC_FACTORY = new Function<ExpressionTypingInternals, BasicExpressionTypingVisitor>() { private static ExpressionTypingVisitorDispatcher BASIC_DISPATCHER = new ExpressionTypingVisitorDispatcher(null);
@Override
public BasicExpressionTypingVisitor fun(ExpressionTypingInternals facade) {
return new BasicExpressionTypingVisitor(facade);
}
};
@NotNull @NotNull
public static ExpressionTypingFacade create() { public static ExpressionTypingFacade create() {
return new ExpressionTypingVisitorDispatcher(BASIC_FACTORY); return BASIC_DISPATCHER;
} }
@NotNull @NotNull
public static ExpressionTypingInternals createForBlock(final WritableScope writableScope) { public static ExpressionTypingInternals createForBlock(final WritableScope writableScope) {
return new ExpressionTypingVisitorDispatcher(new Function<ExpressionTypingInternals, BasicExpressionTypingVisitor>() { return new ExpressionTypingVisitorDispatcher(writableScope);
@Override
public BasicExpressionTypingVisitor fun(ExpressionTypingInternals facade) {
return new ExpressionTypingVisitorForStatements(facade, writableScope);
}
});
} }
private final BasicExpressionTypingVisitor basic; private final BasicExpressionTypingVisitor basic;
private final ExpressionTypingVisitorForStatements statements;
private final ClosureExpressionsTypingVisitor closures = new ClosureExpressionsTypingVisitor(this); private final ClosureExpressionsTypingVisitor closures = new ClosureExpressionsTypingVisitor(this);
private final ControlStructureTypingVisitor controlStructures = new ControlStructureTypingVisitor(this); private final ControlStructureTypingVisitor controlStructures = new ControlStructureTypingVisitor(this);
private final PatternMatchingTypingVisitor patterns = new PatternMatchingTypingVisitor(this); private final PatternMatchingTypingVisitor patterns = new PatternMatchingTypingVisitor(this);
protected DataFlowInfo resultDataFlowInfo; protected DataFlowInfo resultDataFlowInfo;
private ExpressionTypingVisitorDispatcher(Function<ExpressionTypingInternals, BasicExpressionTypingVisitor> factoryForBasic) { private ExpressionTypingVisitorDispatcher(WritableScope writableScope) {
this.basic = factoryForBasic.fun(this); this.basic = new BasicExpressionTypingVisitor(this);
if (writableScope != null) {
this.statements = new ExpressionTypingVisitorForStatements(this, writableScope, basic);
}
else {
this.statements = null;
}
} }
@Override @Override
@@ -87,12 +85,23 @@ public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetType, Expre
@Override @Override
@Nullable @Nullable
public final JetType getType(@NotNull JetExpression expression, ExpressionTypingContext context) { public final JetType getType(@NotNull JetExpression expression, ExpressionTypingContext context) {
return getType(expression, context, this);
}
@Override
@Nullable
public final JetType getTypeForStatement(@NotNull JetExpression expression, ExpressionTypingContext context) {
return getType(expression, context, statements);
}
@Nullable
private JetType getType(@NotNull JetExpression expression, ExpressionTypingContext context, JetVisitor<JetType, ExpressionTypingContext> visitor) {
if (context.trace.get(BindingContext.PROCESSED, expression)) { if (context.trace.get(BindingContext.PROCESSED, expression)) {
return context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression); return context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression);
} }
JetType result; JetType result;
try { try {
result = expression.accept(this, context); result = expression.accept(visitor, context);
// Some recursive definitions (object expressions) must put their types in the cache manually: // Some recursive definitions (object expressions) must put their types in the cache manually:
if (context.trace.get(BindingContext.PROCESSED, expression)) { if (context.trace.get(BindingContext.PROCESSED, expression)) {
return context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression); return context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression);
@@ -115,10 +124,10 @@ public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetType, Expre
context.trace.record(BindingContext.RESOLUTION_SCOPE, expression, context.scope); context.trace.record(BindingContext.RESOLUTION_SCOPE, expression, context.scope);
} }
context.trace.record(BindingContext.PROCESSED, expression); context.trace.record(BindingContext.PROCESSED, expression);
return result; return result;
} }
////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////
@Override @Override
public JetType visitFunctionLiteralExpression(JetFunctionLiteralExpression expression, ExpressionTypingContext data) { public JetType visitFunctionLiteralExpression(JetFunctionLiteralExpression expression, ExpressionTypingContext data) {
@@ -27,12 +27,14 @@ import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.get
/** /**
* @author abreslav * @author abreslav
*/ */
public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingVisitor { public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisitor {
private final WritableScope scope; private final WritableScope scope;
private final BasicExpressionTypingVisitor basic;
public ExpressionTypingVisitorForStatements(@NotNull ExpressionTypingInternals facade, @NotNull WritableScope scope) { public ExpressionTypingVisitorForStatements(@NotNull ExpressionTypingInternals facade, @NotNull WritableScope scope, BasicExpressionTypingVisitor basic) {
super(facade); super(facade);
this.scope = scope; this.scope = scope;
this.basic = basic;
} }
@Nullable @Nullable
@@ -126,6 +128,22 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
} }
@Override @Override
public JetType visitBinaryExpression(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
JetSimpleNameExpression operationSign = expression.getOperationReference();
IElementType operationType = operationSign.getReferencedNameElementType();
JetType result;
if (operationType == JetTokens.EQ) {
result = visitAssignment(expression, contextWithExpectedType);
}
else if (OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(operationType)) {
result = visitAssignmentOperation(expression, contextWithExpectedType);
}
else {
return facade.getType(expression, contextWithExpectedType);
}
return DataFlowUtils.checkType(result, expression, contextWithExpectedType);
}
protected JetType visitAssignmentOperation(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) { protected JetType visitAssignmentOperation(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceExpectedReturnType(TypeUtils.NO_EXPECTED_TYPE); ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceExpectedReturnType(TypeUtils.NO_EXPECTED_TYPE);
// If there's += (or similar op) defined as such, we just call it. // If there's += (or similar op) defined as such, we just call it.
@@ -134,7 +152,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
String name = OperatorConventions.ASSIGNMENT_OPERATIONS.get(operationType); String name = OperatorConventions.ASSIGNMENT_OPERATIONS.get(operationType);
TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace.create(context.trace); TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace.create(context.trace);
JetType assignmentOperationType = getTypeForBinaryCall(scope, name, context.replaceBindingTrace(temporaryBindingTrace), expression); JetType assignmentOperationType = basic.getTypeForBinaryCall(scope, name, context.replaceBindingTrace(temporaryBindingTrace), expression);
JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft()); JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft());
// If there isn't, we call plus (or like) and then assign // If there isn't, we call plus (or like) and then assign
@@ -145,7 +163,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left; JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left;
resolveArrayAccessToLValue(arrayAccessExpression, expression.getRight(), operationSign, context); resolveArrayAccessToLValue(arrayAccessExpression, expression.getRight(), operationSign, context);
} }
assignmentOperationType = getTypeForBinaryCall(scope, counterpartName, context, expression); assignmentOperationType = basic.getTypeForBinaryCall(scope, counterpartName, context, expression);
if (assignmentOperationType != null) { if (assignmentOperationType != null) {
context.trace.record(VARIABLE_REASSIGNMENT, expression); context.trace.record(VARIABLE_REASSIGNMENT, expression);
ExpressionTypingUtils.checkWrappingInRef(expression.getLeft(), context); ExpressionTypingUtils.checkWrappingInRef(expression.getLeft(), context);
@@ -155,7 +173,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
temporaryBindingTrace.commit(); temporaryBindingTrace.commit();
checkReassignment(expression, context, assignmentOperationType, left); checkReassignment(expression, context, assignmentOperationType, left);
} }
checkLValue(context.trace, expression.getLeft()); basic.checkLValue(context.trace, expression.getLeft());
return checkAssignmentType(assignmentOperationType, expression, contextWithExpectedType); return checkAssignmentType(assignmentOperationType, expression, contextWithExpectedType);
} }
@@ -176,7 +194,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
} }
} }
@Override
protected JetType visitAssignment(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) { protected JetType visitAssignment(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceExpectedReturnType(TypeUtils.NO_EXPECTED_TYPE); ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceExpectedReturnType(TypeUtils.NO_EXPECTED_TYPE);
JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft()); JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft());
@@ -184,7 +202,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
if (left instanceof JetArrayAccessExpression) { if (left instanceof JetArrayAccessExpression) {
JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left; JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left;
JetType assignmentType = resolveArrayAccessToLValue(arrayAccessExpression, right, expression.getOperationReference(), context); JetType assignmentType = resolveArrayAccessToLValue(arrayAccessExpression, right, expression.getOperationReference(), context);
checkLValue(context.trace, arrayAccessExpression); basic.checkLValue(context.trace, arrayAccessExpression);
return checkAssignmentType(assignmentType, expression, contextWithExpectedType); return checkAssignmentType(assignmentType, expression, contextWithExpectedType);
} }
JetType leftType = facade.getType(expression.getLeft(), context.replaceScope(scope)); JetType leftType = facade.getType(expression.getLeft(), context.replaceScope(scope));
@@ -192,7 +210,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
JetType rightType = facade.getType(right, context.replaceExpectedType(leftType).replaceScope(scope)); JetType rightType = facade.getType(right, context.replaceExpectedType(leftType).replaceScope(scope));
} }
if (leftType != null) { //if leftType == null, some another error has been generated if (leftType != null) { //if leftType == null, some another error has been generated
checkLValue(context.trace, expression.getLeft()); basic.checkLValue(context.trace, expression.getLeft());
} }
if (left instanceof JetSimpleNameExpression) { if (left instanceof JetSimpleNameExpression) {
JetSimpleNameExpression simpleName = (JetSimpleNameExpression) left; JetSimpleNameExpression simpleName = (JetSimpleNameExpression) left;
@@ -237,6 +255,11 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
return setFunctionDescriptor.getReturnType(); return setFunctionDescriptor.getReturnType();
} }
@Override
public JetType visitExpression(JetExpression expression, ExpressionTypingContext context) {
return facade.getType(expression, context);
}
@Override @Override
public JetType visitJetElement(JetElement element, ExpressionTypingContext context) { public JetType visitJetElement(JetElement element, ExpressionTypingContext context) {
context.trace.report(UNSUPPORTED.on(element, "in a block")); context.trace.report(UNSUPPORTED.on(element, "in a block"));
@@ -0,0 +1,23 @@
//KT-629 Assignments are parsed as expressions.
namespace kt629
class A() {
var p = "yeah"
fun mod(other : A) : A {
return A();
}
}
fun box() : Boolean {
var c = A()
val d = c;
c %= A();
return (c != d) && (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>c.p = "yeah"<!>)
}
fun box2() : Boolean {
var <!UNUSED_VARIABLE!>c<!> = A()
return (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>c.p = "yeah"<!>) && true
}
@@ -113,7 +113,7 @@ public class JetTypeCheckerTest extends JetLiteFixture {
assertType("if (true) 1 else '1'", "Any"); assertType("if (true) 1 else '1'", "Any");
assertType("if (true) else '1'", "Unit"); assertType("if (true) else '1'", "Unit");
assertType("if (true) else a = 0", "Unit"); assertType("if (true) else { var a = 0; a = 1 }", "Unit");
} }
public void testWhen() throws Exception { public void testWhen() throws Exception {