From 3cacb512a32c4de9d13362f13e49747166a975ce Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Mon, 13 Jun 2011 14:33:32 +0400 Subject: [PATCH] Working on bindings in ifs --- .../jet/lang/resolve/WritableScopeImpl.java | 16 ++ .../jetbrains/jet/lang/types/ErrorUtils.java | 1 + .../jet/lang/types/JetTypeInferrer.java | 141 ++++++++++++------ idea/testData/checker/IsExpressions.jet | 2 +- idea/testData/checker/infos/Autocasts.jet | 10 ++ .../regression/AssignmentsUnderOperators.jet | 6 + 6 files changed, 128 insertions(+), 48 deletions(-) create mode 100644 idea/testData/checker/regression/AssignmentsUnderOperators.jet diff --git a/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeImpl.java b/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeImpl.java index b8e44c2b7cb..9bcf33e9060 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeImpl.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeImpl.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.lang.resolve; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -258,4 +259,19 @@ public class WritableScopeImpl extends WritableScopeWithImports { if (descriptor != null) return descriptor; return super.getPropertyByFieldReference(fieldName); } + + public List getDeclaredVariables() { + List result = Lists.newArrayList(); + for (DeclarationDescriptor descriptor : getVariableClassOrNamespaceDescriptors().values()) { + if (descriptor instanceof VariableDescriptor) { + VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor; + result.add(variableDescriptor); + } + } + return result; + } + + public boolean hasDeclaredItems() { + return variableClassOrNamespaceDescriptors != null && !variableClassOrNamespaceDescriptors.isEmpty(); + } } diff --git a/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java b/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java index d9d282d1a2f..b2be863254a 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java +++ b/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java @@ -155,6 +155,7 @@ public class ErrorUtils { return result; } + @NotNull public static JetType createErrorType(String debugMessage) { return createErrorType(debugMessage, ERROR_SCOPE); } diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 436d3e30182..45918050f9b 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -72,6 +72,8 @@ public class JetTypeInferrer { private final TypeResolver typeResolver; private final ClassDescriptorResolver classDescriptorResolver; private final JetFlowInformationProvider flowInformationProvider; + private final Map patternsToDataFlowInfo = Maps.newHashMap(); + private final Map> patternsToBoundVariableLists = Maps.newHashMap(); public JetTypeInferrer(@NotNull BindingTrace trace, @NotNull JetFlowInformationProvider flowInformationProvider, @NotNull JetSemanticServices semanticServices) { this.trace = trace; //new CachedBindingTrace(trace); @@ -368,8 +370,15 @@ public class JetTypeInferrer { JetExpression statementExpression = (JetExpression) statement; result = blockLevelVisitor.getType(statementExpression); DataFlowInfo newDataFlowInfo = blockLevelVisitor.getResultingDataFlowInfo(); - if (newDataFlowInfo != null) { - blockLevelVisitor = new TypeInferrerVisitorWithWritableScope(scope, true, newDataFlowInfo); + if (newDataFlowInfo == null) { + newDataFlowInfo = dataFlowInfo; + } + WritableScope newScope = blockLevelVisitor.getResultScope(); + if (newScope == null) { + newScope = scope; + } + if (newDataFlowInfo != dataFlowInfo || newScope != scope) { + blockLevelVisitor = new TypeInferrerVisitorWithWritableScope(newScope, true, newDataFlowInfo); } else { blockLevelVisitor.resetResult(); // TODO : maybe it's better to recreate the visitors with the same scope? @@ -582,6 +591,7 @@ public class JetTypeInferrer { protected JetType result; protected DataFlowInfo resultDataFlowInfo; + protected WritableScope resultScope; private TypeInferrerVisitor(@NotNull JetScope scope, boolean preferBlock, @NotNull DataFlowInfo dataFlowInfo) { this.scope = scope; @@ -594,6 +604,16 @@ public class JetTypeInferrer { return resultDataFlowInfo; } + public WritableScope getResultScope() { + if (resultScope instanceof WritableScopeImpl) { + WritableScopeImpl writableScope = (WritableScopeImpl) resultScope; + if (!writableScope.hasDeclaredItems()) { + return null; + } + } + return resultScope; + } + @Nullable public JetType getType(@NotNull JetScope scope, @NotNull JetExpression expression, boolean preferBlock) { return getType(scope, expression, preferBlock, dataFlowInfo); @@ -652,6 +672,8 @@ public class JetTypeInferrer { public void resetResult() { result = null; + resultDataFlowInfo = null; + resultScope = null; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1096,7 +1118,7 @@ public class JetTypeInferrer { Set expressionTypes = Sets.newHashSet(); for (JetWhenEntry whenEntry : expression.getEntries()) { JetWhenCondition condition = whenEntry.getCondition(); - WritableScope scopeToExtend = new WritableScopeImpl(scope, scope.getContainingDeclaration(), trace.getErrorHandler()); + WritableScope scopeToExtend = newWritableScopeImpl(); DataFlowInfo newDataFlowInfo = dataFlowInfo; if (condition != null) { newDataFlowInfo = checkWhenCondition(subjectExpression, subjectType, condition, scopeToExtend, variableDescriptor); @@ -1225,7 +1247,7 @@ public class JetTypeInferrer { @Override public void visitExpressionPattern(JetExpressionPattern pattern) { - JetType type = getType(scope, pattern.getExpression(), false); + JetType type = getType(scopeToExtend, pattern.getExpression(), false); checkTypeCompatibility(type, subjectType, pattern); } @@ -1282,7 +1304,7 @@ public class JetTypeInferrer { if (catchParameter != null) { VariableDescriptor variableDescriptor = classDescriptorResolver.resolveLocalVariableDescriptor(scope.getContainingDeclaration(), scope, catchParameter); if (catchBody != null) { - WritableScope catchScope = new WritableScopeImpl(scope, scope.getContainingDeclaration(), trace.getErrorHandler()); + WritableScope catchScope = newWritableScopeImpl(); catchScope.addVariableDescriptor(variableDescriptor); JetType type = getType(catchScope, catchBody, true); if (type != null) { @@ -1318,12 +1340,14 @@ public class JetTypeInferrer { JetExpression elseBranch = expression.getElse(); JetExpression thenBranch = expression.getThen(); - DataFlowInfo thenInfo = extractDataFlowInfoFromCondition(condition, true); - DataFlowInfo elseInfo = extractDataFlowInfoFromCondition(condition, false); + WritableScopeImpl thenScope = newWritableScopeImpl(); + DataFlowInfo thenInfo = extractDataFlowInfoFromCondition(condition, true, thenScope); + WritableScopeImpl elseScope = newWritableScopeImpl(); + DataFlowInfo elseInfo = extractDataFlowInfoFromCondition(condition, false, elseScope); if (elseBranch == null) { if (thenBranch != null) { - JetType type = getType(scope, thenBranch, true, thenInfo); + JetType type = getType(thenScope, thenBranch, true, thenInfo); if (type != null && JetStandardClasses.isNothing(type)) { resultDataFlowInfo = elseInfo; } @@ -1331,15 +1355,15 @@ public class JetTypeInferrer { } } else if (thenBranch == null) { - JetType type = getType(scope, elseBranch, true, elseInfo); + JetType type = getType(elseScope, elseBranch, true, elseInfo); if (type != null && JetStandardClasses.isNothing(type)) { resultDataFlowInfo = thenInfo; } result = JetStandardClasses.getUnitType(); } else { - JetType thenType = getType(scope, thenBranch, true, thenInfo); - JetType elseType = getType(scope, elseBranch, true, elseInfo); + JetType thenType = getType(thenScope, thenBranch, true, thenInfo); + JetType elseType = getType(elseScope, elseBranch, true, elseInfo); if (thenType == null) { result = elseType; @@ -1364,21 +1388,35 @@ public class JetTypeInferrer { } } - private DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue) { + private DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, @NotNull final WritableScope scopeToExtend) { if (condition == null) return dataFlowInfo; final DataFlowInfo[] result = new DataFlowInfo[] {dataFlowInfo}; condition.accept(new JetVisitor() { @Override public void visitIsExpression(JetIsExpression expression) { if (conditionValue) { - VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(expression.getLeftHandSide()); - if (variableDescriptor != null) { - JetPattern pattern = expression.getPattern(); - if (pattern instanceof JetTypePattern) { - JetTypePattern jetTypePattern = (JetTypePattern) pattern; - result[0] = dataFlowInfo.isInstanceOf(variableDescriptor, trace.getBindingContext().resolveTypeReference(jetTypePattern.getTypeReference())); - } + JetPattern pattern = expression.getPattern(); + for (VariableDescriptor variableDescriptor : patternsToBoundVariableLists.get(pattern)) { + scopeToExtend.addVariableDescriptor(variableDescriptor); } + result[0] = patternsToDataFlowInfo.get(pattern); + +// VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(expression.getLeftHandSide()); +// if (variableDescriptor != null && pattern != null) { +// JetType outType = variableDescriptor.getOutType(); +// if (outType == null) { +// outType = ErrorUtils.createErrorType(""); +// } +// trace.getErrorHandler().openRegion(); +// result[0] = checkPatternType(pattern, outType, scopeToExtend, variableDescriptor); +// trace.getErrorHandler().closeAndReturnCurrentRegion(); +// +// } +//// if (pattern instanceof JetTypePattern) { +//// JetTypePattern jetTypePattern = (JetTypePattern) pattern; +//// result[0] = dataFlowInfo.isInstanceOf(variableDescriptor, trace.getBindingContext().resolveTypeReference(jetTypePattern.getTypeReference())); +//// } +//// } } } @@ -1386,10 +1424,10 @@ public class JetTypeInferrer { public void visitBinaryExpression(JetBinaryExpression expression) { IElementType operationToken = expression.getOperationToken(); if (operationToken == JetTokens.ANDAND || operationToken == JetTokens.OROR) { - DataFlowInfo dataFlowInfo = extractDataFlowInfoFromCondition(expression.getLeft(), conditionValue); + DataFlowInfo dataFlowInfo = extractDataFlowInfoFromCondition(expression.getLeft(), conditionValue, scopeToExtend); // TODO JetExpression expressionRight = expression.getRight(); if (expressionRight != null) { - DataFlowInfo rightInfo = extractDataFlowInfoFromCondition(expressionRight, conditionValue); + DataFlowInfo rightInfo = extractDataFlowInfoFromCondition(expressionRight, conditionValue, scopeToExtend); // TODO DataFlowInfo.CompositionOperator operator; if (operationToken == JetTokens.ANDAND) { operator = conditionValue ? DataFlowInfo.AND : DataFlowInfo.OR; @@ -1467,7 +1505,7 @@ public class JetTypeInferrer { if (operationTokenType == JetTokens.EXCL) { JetExpression baseExpression = expression.getBaseExpression(); if (baseExpression != null) { - result[0] = extractDataFlowInfoFromCondition(baseExpression, !conditionValue); + result[0] = extractDataFlowInfoFromCondition(baseExpression, !conditionValue, scopeToExtend); // TODO } } } @@ -1499,11 +1537,13 @@ public class JetTypeInferrer { checkCondition(scope, condition); JetExpression body = expression.getBody(); if (body != null) { - DataFlowInfo conditionInfo = condition == null ? dataFlowInfo : extractDataFlowInfoFromCondition(condition, true); - getType(scope, body, true, conditionInfo); + WritableScopeImpl scopeToExtend = newWritableScopeImpl(); + DataFlowInfo conditionInfo = condition == null ? dataFlowInfo : extractDataFlowInfoFromCondition(condition, true, scopeToExtend); + getType(scopeToExtend, body, true, conditionInfo); } if (!flowInformationProvider.isBreakable(expression)) { - resultDataFlowInfo = extractDataFlowInfoFromCondition(condition, false); + resultScope = newWritableScopeImpl(); + resultDataFlowInfo = extractDataFlowInfoFromCondition(condition, false, resultScope); } result = JetStandardClasses.getUnitType(); } @@ -1515,7 +1555,7 @@ public class JetTypeInferrer { if (body instanceof JetFunctionLiteralExpression) { JetFunctionLiteralExpression function = (JetFunctionLiteralExpression) body; if (!function.hasParameterSpecification()) { - WritableScope writableScope = new WritableScopeImpl(scope, scope.getContainingDeclaration(), trace.getErrorHandler()); + WritableScope writableScope = newWritableScopeImpl(); conditionScope = writableScope; getBlockReturnedTypeWithWritableScope(writableScope, function.getBody(), dataFlowInfo); trace.recordBlock(function); @@ -1524,18 +1564,23 @@ public class JetTypeInferrer { } } else if (body != null) { - WritableScope writableScope = new WritableScopeImpl(scope, scope.getContainingDeclaration(), trace.getErrorHandler()); + WritableScope writableScope = newWritableScopeImpl(); conditionScope = writableScope; getBlockReturnedTypeWithWritableScope(writableScope, Collections.singletonList(body), dataFlowInfo); } JetExpression condition = expression.getCondition(); checkCondition(conditionScope, condition); if (!flowInformationProvider.isBreakable(expression)) { - resultDataFlowInfo = extractDataFlowInfoFromCondition(condition, false); + resultScope = newWritableScopeImpl(); + resultDataFlowInfo = extractDataFlowInfoFromCondition(condition, false, resultScope); } result = JetStandardClasses.getUnitType(); } + protected WritableScopeImpl newWritableScopeImpl() { + return new WritableScopeImpl(scope, scope.getContainingDeclaration(), trace.getErrorHandler()); + } + @Override public void visitForExpression(JetForExpression expression) { JetParameter loopParameter = expression.getLoopParameter(); @@ -1549,7 +1594,7 @@ public class JetTypeInferrer { expectedParameterType = checkIterableConvention(loopRangeType, loopRange.getNode()); } - WritableScope loopScope = new WritableScopeImpl(scope, scope.getContainingDeclaration(), trace.getErrorHandler()); + WritableScope loopScope = newWritableScopeImpl(); if (loopParameter != null) { JetTypeReference typeReference = loopParameter.getTypeReference(); @@ -1733,6 +1778,12 @@ public class JetTypeInferrer { @Nullable private VariableDescriptor getVariableDescriptorFromSimpleName(@NotNull JetExpression receiverExpression) { + if (receiverExpression instanceof JetBinaryExpressionWithTypeRHS) { + JetBinaryExpressionWithTypeRHS expression = (JetBinaryExpressionWithTypeRHS) receiverExpression; + if (expression.getOperationSign().getReferencedNameElementType() == JetTokens.COLON) { + return getVariableDescriptorFromSimpleName(expression.getLeft()); + } + } VariableDescriptor variableDescriptor = null; if (receiverExpression instanceof JetSimpleNameExpression) { JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) receiverExpression; @@ -1815,27 +1866,14 @@ public class JetTypeInferrer { @Override public void visitIsExpression(JetIsExpression expression) { // TODO : patterns and everything - // TODO : Unify with WhenExpression JetType knownType = getType(scope, expression.getLeftHandSide(), false); JetPattern pattern = expression.getPattern(); if (pattern != null && knownType != null) { - checkPatternType(pattern, knownType, new WritableScopeImpl(scope, scope.getContainingDeclaration(), trace.getErrorHandler())); + WritableScopeImpl scopeToExtend = newWritableScopeImpl(); + DataFlowInfo newDataFlowInfo = checkPatternType(pattern, knownType, scopeToExtend, getVariableDescriptorFromSimpleName(expression.getLeftHandSide())); + patternsToDataFlowInfo.put(pattern, newDataFlowInfo); + patternsToBoundVariableLists.put(pattern, scopeToExtend.getDeclaredVariables()); } -// if (pattern instanceof JetTypePattern) { -// JetTypePattern typePattern = (JetTypePattern) pattern; -// JetTypeReference typeReference = typePattern.getTypeReference(); -// if (typeReference != null && knownType != null) { -// JetType targetType = typeResolver.resolveType(scope, typeReference); -// if (!semanticServices.getTypeChecker().isSubtypeOf(targetType, knownType)) { -// trace.getErrorHandler().genericWarning(expression.getNode(), "Expression always evaluates to false"); // TODO : make an error? -// } -// } -// } -// else { -// if (pattern != null) { -// trace.getErrorHandler().genericError(pattern.getNode(), "Unsupported [JetTypeInferrer]"); -// } -// } result = semanticServices.getStandardLibrary().getBooleanType(); } @@ -1952,7 +1990,8 @@ public class JetTypeInferrer { } else if (operationType == JetTokens.ANDAND || operationType == JetTokens.OROR) { JetType leftType = getType(scope, left, false); - DataFlowInfo flowInfoLeft = extractDataFlowInfoFromCondition(left, operationType == JetTokens.ANDAND); // TODO: This gets computed twice: here and in extractDataFlowInfoFromCondition() for the whole condition + // TODO!!! + DataFlowInfo flowInfoLeft = extractDataFlowInfoFromCondition(left, operationType == JetTokens.ANDAND, newWritableScopeImpl()); // TODO: This gets computed twice: here and in extractDataFlowInfoFromCondition() for the whole condition JetType rightType = right == null ? null : getType(scope, right, false, flowInfoLeft); if (leftType != null && !isBoolean(leftType)) { trace.getErrorHandler().typeMismatch(left, semanticServices.getStandardLibrary().getBooleanType(), leftType); @@ -2293,6 +2332,14 @@ public class JetTypeInferrer { trace.getErrorHandler().genericError(element.getNode(), "Unsupported element in a block: " + element + " " + element.getClass().getCanonicalName()); } + @NotNull + @Override + public TypeInferrerVisitor createNew(JetScope scope, boolean preferBlock, DataFlowInfo dataFlowInfo) { +// if (scope instanceof WritableScope) { +// return new TypeInferrerVisitorWithWritableScope((WritableScope) scope, preferBlock, dataFlowInfo); +// } + return new TypeInferrerVisitorWithWritableScope(new WritableScopeImpl(scope, scope.getContainingDeclaration(), trace.getErrorHandler()), preferBlock, dataFlowInfo); + } } // private class CachedBindingTrace extends BindingTraceAdapter { diff --git a/idea/testData/checker/IsExpressions.jet b/idea/testData/checker/IsExpressions.jet index a88f9578186..877556791a1 100644 --- a/idea/testData/checker/IsExpressions.jet +++ b/idea/testData/checker/IsExpressions.jet @@ -1,6 +1,6 @@ fun test() { if (1 is Int) { - if (1 is Boolean) { + if (1 is Boolean) { } } diff --git a/idea/testData/checker/infos/Autocasts.jet b/idea/testData/checker/infos/Autocasts.jet index 7116b2cedf3..84b6a1a53de 100644 --- a/idea/testData/checker/infos/Autocasts.jet +++ b/idea/testData/checker/infos/Autocasts.jet @@ -75,4 +75,14 @@ fun f12(a : A?) { is val c is C => a.bar() else => a?.foo() } + + if (a is val b) { + a?.bar() + b?.foo() + } + if (a is val b is B) { + b.foo() + a.bar() + b.bar() + } } diff --git a/idea/testData/checker/regression/AssignmentsUnderOperators.jet b/idea/testData/checker/regression/AssignmentsUnderOperators.jet new file mode 100644 index 00000000000..5a7c24b7fd0 --- /dev/null +++ b/idea/testData/checker/regression/AssignmentsUnderOperators.jet @@ -0,0 +1,6 @@ +fun test() { + val a : Any? = null + if (a is Any) else a = null; + while (a is Any) a = null + while (true) a = null +} \ No newline at end of file