diff --git a/idea/src/org/jetbrains/jet/lang/types/DataFlowInfo.java b/idea/src/org/jetbrains/jet/lang/types/DataFlowInfo.java index 2de1d738c35..8cf34eb367a 100644 --- a/idea/src/org/jetbrains/jet/lang/types/DataFlowInfo.java +++ b/idea/src/org/jetbrains/jet/lang/types/DataFlowInfo.java @@ -80,12 +80,34 @@ public class DataFlowInfo { return ImmutableMap.copyOf(builder); } + private ImmutableMap getEqualsToNullMap(VariableDescriptor[] variableDescriptors, boolean notNull) { + if (variableDescriptors.length == 0) return nullabilityInfo; + Map builder = Maps.newHashMap(nullabilityInfo); + for (VariableDescriptor variableDescriptor : variableDescriptors) { + if (variableDescriptor != null) { + builder.put(variableDescriptor, new NullabilityFlags(!notNull, notNull)); + } + } + return ImmutableMap.copyOf(builder); + } + public DataFlowInfo isInstanceOf(@NotNull VariableDescriptor variableDescriptor, @NotNull JetType type) { ListMultimap newTypeInfo = copyTypeInfo(); newTypeInfo.put(variableDescriptor, type); return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, !type.isNullable()), newTypeInfo); } + public DataFlowInfo isInstanceOf(@NotNull VariableDescriptor[] variableDescriptors, @NotNull JetType type) { + if (variableDescriptors.length == 0) return this; + ListMultimap newTypeInfo = copyTypeInfo(); + for (VariableDescriptor variableDescriptor : variableDescriptors) { + if (variableDescriptor != null) { + newTypeInfo.put(variableDescriptor, type); + } + } + return new DataFlowInfo(getEqualsToNullMap(variableDescriptors, !type.isNullable()), newTypeInfo); + } + public DataFlowInfo and(DataFlowInfo other) { Map nullabilityMapBuilder = Maps.newHashMap(); nullabilityMapBuilder.putAll(nullabilityInfo); diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index c8a44bd90b5..436d3e30182 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -1099,7 +1099,7 @@ public class JetTypeInferrer { WritableScope scopeToExtend = new WritableScopeImpl(scope, scope.getContainingDeclaration(), trace.getErrorHandler()); DataFlowInfo newDataFlowInfo = dataFlowInfo; if (condition != null) { - newDataFlowInfo = checkWhenCondition(subjectExpression, subjectType, variableDescriptor, condition, scopeToExtend); + newDataFlowInfo = checkWhenCondition(subjectExpression, subjectType, condition, scopeToExtend, variableDescriptor); } JetWhenExpression subWhen = whenEntry.getSubWhen(); JetExpression bodyExpression = subWhen == null ? whenEntry.getExpression() : subWhen; @@ -1119,7 +1119,7 @@ public class JetTypeInferrer { } } - private DataFlowInfo checkWhenCondition(@Nullable final JetExpression subjectExpression, final JetType subjectType, final VariableDescriptor variableDescriptor, JetWhenCondition condition, final WritableScope scopeToExtend) { + private DataFlowInfo checkWhenCondition(@Nullable final JetExpression subjectExpression, final JetType subjectType, JetWhenCondition condition, final WritableScope scopeToExtend, final VariableDescriptor... subjectVariables) { final DataFlowInfo[] newDataFlowInfo = new DataFlowInfo[]{dataFlowInfo}; condition.accept(new JetVisitor() { @Override @@ -1159,7 +1159,7 @@ public class JetTypeInferrer { public void visitWhenConditionIsPattern(JetWhenConditionIsPattern condition) { JetPattern pattern = condition.getPattern(); if (pattern != null) { - newDataFlowInfo[0] = checkPatternType(variableDescriptor, pattern, subjectType, scopeToExtend); + newDataFlowInfo[0] = checkPatternType(pattern, subjectType, scopeToExtend, subjectVariables); } } @@ -1171,7 +1171,7 @@ public class JetTypeInferrer { return newDataFlowInfo[0]; } - private DataFlowInfo checkPatternType(@Nullable final VariableDescriptor subjectVariable, @NotNull JetPattern pattern, @NotNull final JetType subjectType, @NotNull final WritableScope scopeToExtend) { + private DataFlowInfo checkPatternType(@NotNull JetPattern pattern, @NotNull final JetType subjectType, @NotNull final WritableScope scopeToExtend, @NotNull final VariableDescriptor... subjectVariables) { final DataFlowInfo[] result = new DataFlowInfo[] {dataFlowInfo}; pattern.accept(new JetVisitor() { @Override @@ -1180,9 +1180,7 @@ public class JetTypeInferrer { if (typeReference != null) { JetType type = typeResolver.resolveType(scope, typeReference); checkTypeCompatibility(type, subjectType, typePattern); - if (subjectVariable != null) { - result[0] = dataFlowInfo.isInstanceOf(subjectVariable, type); - } + result[0] = dataFlowInfo.isInstanceOf(subjectVariables, type); } } @@ -1207,7 +1205,7 @@ public class JetTypeInferrer { JetPattern entryPattern = entry.getPattern(); if (entryPattern != null) { - result[0] = checkPatternType(null, entryPattern, type, scopeToExtend); + result[0] = checkPatternType(entryPattern, type, scopeToExtend); } } } @@ -1217,7 +1215,7 @@ public class JetTypeInferrer { public void visitDecomposerPattern(JetDecomposerPattern pattern) { JetType selectorReturnType = getSelectorReturnType(subjectType, pattern.getDecomposerExpression()); - result[0] = checkPatternType(null, pattern.getArgumentList(), selectorReturnType == null ? ErrorUtils.createErrorType("No type") : selectorReturnType, scopeToExtend); + result[0] = checkPatternType(pattern.getArgumentList(), selectorReturnType == null ? ErrorUtils.createErrorType("No type") : selectorReturnType, scopeToExtend); } @Override @@ -1246,7 +1244,11 @@ public class JetTypeInferrer { JetWhenCondition condition = pattern.getCondition(); if (condition != null) { - result[0] = checkWhenCondition(null, subjectType, variableDescriptor, condition, scopeToExtend); + int oldLength = subjectVariables.length; + VariableDescriptor[] newSubjectVariables = new VariableDescriptor[oldLength + 1]; + System.arraycopy(subjectVariables, 0, newSubjectVariables, 0, oldLength); + newSubjectVariables[oldLength] = variableDescriptor; + result[0] = checkWhenCondition(null, subjectType, condition, scopeToExtend, newSubjectVariables); } } @@ -1816,21 +1818,24 @@ public class JetTypeInferrer { // TODO : Unify with WhenExpression JetType knownType = getType(scope, expression.getLeftHandSide(), false); JetPattern pattern = expression.getPattern(); - 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]"); - } + if (pattern != null && knownType != null) { + checkPatternType(pattern, knownType, new WritableScopeImpl(scope, scope.getContainingDeclaration(), trace.getErrorHandler())); } +// 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(); } diff --git a/idea/testData/checker/infos/Autocasts.jet b/idea/testData/checker/infos/Autocasts.jet index 54a4aaa47a7..7116b2cedf3 100644 --- a/idea/testData/checker/infos/Autocasts.jet +++ b/idea/testData/checker/infos/Autocasts.jet @@ -71,7 +71,8 @@ fun f12(a : A?) { is Any => a.foo(); is Any? => a.bar() is val c : B => c.foo() - is val c is C => c.foo() + is val c is C => c.bar() + is val c is C => a.bar() else => a?.foo() } }