diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java index 9051c404887..bee520f5297 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java @@ -9,7 +9,9 @@ import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.JetModuleUtil; import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiverDescriptor; +import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.JetTypeChecker; import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET; @@ -27,6 +29,7 @@ public class DataFlowValueFactory { JetConstantExpression constantExpression = (JetConstantExpression) expression; if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL; } + if (JetTypeChecker.INSTANCE.equalTypes(type, JetStandardClasses.getNullableNothingType())) return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?' Pair result = getIdForStableIdentifier(expression, bindingContext, false); return new DataFlowValue(result.first == null ? expression : result.first, type, result.second, getImmanentNullability(type)); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardClasses.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardClasses.java index 41774dc14c4..70db11221a4 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardClasses.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardClasses.java @@ -39,12 +39,12 @@ public class JetStandardClasses { @Override public Iterator iterator() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("Don't enumerate supertypes of Nothing"); } @Override public int size() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("Supertypes of Nothing do not constitute a valid collection"); } }, JetScope.EMPTY, @@ -278,8 +278,13 @@ public class JetStandardClasses { } public static boolean isNothing(@NotNull JetType type) { - return !(type instanceof NamespaceType) && - type.getConstructor() == NOTHING_CLASS.getTypeConstructor(); + return isNothingOrNullableNothing(type) + && !type.isNullable(); + } + + public static boolean isNothingOrNullableNothing(@NotNull JetType type) { + return !(type instanceof NamespaceType) + && type.getConstructor() == NOTHING_CLASS.getTypeConstructor(); } public static boolean isUnit(@NotNull JetType type) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index bd6ecce87ca..d3a36c2652c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -81,7 +81,7 @@ public class JetTypeChecker { JetType type = iterator.next(); assert type != null; // TODO : This admits 'Nothing?'. Review - if (JetStandardClasses.isNothing(type)) { + if (JetStandardClasses.isNothingOrNullableNothing(type)) { iterator.remove(); } nullable |= type.isNullable(); @@ -564,7 +564,7 @@ public class JetTypeChecker { if (!supertype.isNullable() && subtype.isNullable()) { return fail(); } - if (JetStandardClasses.isNothing(subtype)) { + if (JetStandardClasses.isNothingOrNullableNothing(subtype)) { return StatusAction.DONE_WITH_CURRENT_TYPE; } return StatusAction.PROCEED; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index 3a16b72097b..c4f2f06b8da 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -49,7 +49,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } else { context.trace.record(REFERENCE_TARGET, expression, property); - return checkType(property.getOutType(), expression, context); + return DataFlowUtils.checkType(property.getOutType(), expression, context); } } else { @@ -74,14 +74,14 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { if (result == null) { return ErrorUtils.createErrorType("No class object in " + expression.getReferencedName()); } - return checkType(result, expression, context); + return DataFlowUtils.checkType(result, expression, context); } } JetType[] result = new JetType[1]; TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace); if (furtherNameLookup(expression, referencedName, result, context.replaceBindingTrace(temporaryTrace))) { temporaryTrace.commit(); - return checkType(result[0], expression, context); + return DataFlowUtils.checkType(result[0], expression, context); } // To report NO_CLASS_OBJECT when no namespace found if (classifier != null) { @@ -122,7 +122,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { if (innerExpression == null) { return null; } - return checkType(facade.getType(innerExpression, context.replaceScope(context.scope)), expression, context); + return DataFlowUtils.checkType(facade.getType(innerExpression, context.replaceScope(context.scope)), expression, context); } @Override @@ -162,7 +162,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } else { context.trace.record(BindingContext.COMPILE_TIME_VALUE, expression, value); - return checkType(value.getType(standardLibrary), expression, context); + return DataFlowUtils.checkType(value.getType(standardLibrary), expression, context); } } @@ -197,7 +197,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { else { facade.getType(expression.getLeft(), context.replaceExpectedType(NO_EXPECTED_TYPE)); } - return checkType(result, expression, context); + return DataFlowUtils.checkType(result, expression, context); } private boolean checkBinaryWithTypeRHS(JetBinaryExpressionWithTypeRHS expression, ExpressionTypingContext context, @NotNull JetType targetType, @NotNull JetType expectedType, TemporaryBindingTrace temporaryTrace) { @@ -259,7 +259,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } } // TODO : labels - return checkType(JetStandardClasses.getTupleType(types), expression, context); + return DataFlowUtils.checkType(JetStandardClasses.getTupleType(types), expression, context); } @NotNull @@ -269,7 +269,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } List result = Lists.newArrayListWithCapacity(arguments.size()); for (int i = 0, argumentTypesSize = argumentTypes.size(); i < argumentTypesSize; i++) { - result.add(checkType(argumentTypes.get(i), arguments.get(i), context.replaceExpectedType(expectedArgumentTypes.get(i).getType()))); + result.add(DataFlowUtils.checkType(argumentTypes.get(i), arguments.get(i), context.replaceExpectedType(expectedArgumentTypes.get(i).getType()))); } return result; } @@ -330,7 +330,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } } } - return checkType(result, expression, context); + return DataFlowUtils.checkType(result, expression, context); } @Override @@ -386,7 +386,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { if (result != null) { context.trace.record(BindingContext.EXPRESSION_TYPE, selectorExpression, result); } - return checkType(result, expression, contextWithExpectedType); + return DataFlowUtils.checkType(result, expression, contextWithExpectedType); } private void propagateConstantValues(JetQualifiedExpression expression, ExpressionTypingContext context, JetSimpleNameExpression selectorExpression) { @@ -432,14 +432,14 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { VariableDescriptor variableDescriptor = context.replaceBindingTrace(temporaryTrace).resolveSimpleProperty(receiver, callOperationNode, nameExpression); if (variableDescriptor != null) { temporaryTrace.commit(); - return checkType(variableDescriptor.getOutType(), nameExpression, context); + return DataFlowUtils.checkType(variableDescriptor.getOutType(), nameExpression, context); } ExpressionTypingContext newContext = receiver.exists() ? context.replaceScope(receiver.getType().getMemberScope()) : context; JetType jetType = lookupNamespaceOrClassObject(nameExpression, nameExpression.getReferencedName(), newContext); if (jetType == null) { context.trace.report(UNRESOLVED_REFERENCE.on(nameExpression)); } - return checkType(jetType, nameExpression, context); + return DataFlowUtils.checkType(jetType, nameExpression, context); } else if (selectorExpression instanceof JetQualifiedExpression) { JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) selectorExpression; @@ -460,7 +460,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { @Override public JetType visitCallExpression(JetCallExpression expression, ExpressionTypingContext context) { JetType expressionType = context.resolveCall(NO_RECEIVER, null, expression); - return checkType(expressionType, expression, context); + return DataFlowUtils.checkType(expressionType, expression, context); } @Override @@ -473,7 +473,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { referencedName = referencedName == null ? " " : referencedName; context.labelResolver.enterLabeledElement(referencedName.substring(1), baseExpression); // TODO : Some processing for the label? - JetType type = checkType(facade.getType(baseExpression, context.replaceExpectedReturnType(context.expectedType)), expression, context); + JetType type = DataFlowUtils.checkType(facade.getType(baseExpression, context.replaceExpectedReturnType(context.expectedType)), expression, context); context.labelResolver.exitLabeledElement(baseExpression); return type; } @@ -516,7 +516,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { result = returnType; } - return checkType(result, expression, context); + return DataFlowUtils.checkType(result, expression, context); } @Override @@ -599,7 +599,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { 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 = extractDataFlowInfoFromCondition(left, operationType == JetTokens.ANDAND, leftScope, context); // TODO: This gets computed twice: here and in extractDataFlowInfoFromCondition() for the whole condition + 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)) { @@ -618,7 +618,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { context.trace.report(USELESS_ELVIS.on(expression, left, leftType)); } if (rightType != null) { - checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType); + DataFlowUtils.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType); return TypeUtils.makeNullableAsSpecified(context.semanticServices.getTypeChecker().commonSupertype(leftType, rightType), rightType.isNullable()); } } @@ -626,7 +626,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { else { context.trace.report(UNSUPPORTED.on(operationSign, "Unknown operation")); } - return checkType(result, expression, contextWithExpectedType); + return DataFlowUtils.checkType(result, expression, contextWithExpectedType); } public void checkInExpression(JetElement callElement, @NotNull JetSimpleNameExpression operationSign, @NotNull JetExpression left, @NotNull JetExpression right, ExpressionTypingContext context) { @@ -685,7 +685,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { "get", receiver); if (functionDescriptor != null) { - return checkType(functionDescriptor.getReturnType(), expression, contextWithExpectedType); + return DataFlowUtils.checkType(functionDescriptor.getReturnType(), expression, contextWithExpectedType); } } return null; @@ -714,7 +714,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { @Override public JetType visitRootNamespaceExpression(JetRootNamespaceExpression expression, ExpressionTypingContext context) { if (context.namespacesAllowed) { - return ExpressionTypingUtils.checkType(JetModuleUtil.getRootNamespaceType(expression), expression, context); + return DataFlowUtils.checkType(JetModuleUtil.getRootNamespaceType(expression), expression, context); } context.trace.report(NAMESPACE_IS_NOT_AN_EXPRESSION.on(expression)); return null; @@ -764,7 +764,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { if (value[0] != CompileTimeConstantResolver.OUT_OF_RANGE) { context.trace.record(BindingContext.COMPILE_TIME_VALUE, expression, new StringValue(builder.toString())); } - return checkType(context.semanticServices.getStandardLibrary().getStringType(), expression, contextWithExpectedType); + return DataFlowUtils.checkType(context.semanticServices.getStandardLibrary().getStringType(), expression, contextWithExpectedType); } @Override diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java index 1a0f7412042..e0b992f735e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java @@ -55,7 +55,7 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor { ObservableBindingTrace traceAdapter = new ObservableBindingTrace(context.trace); traceAdapter.addHandler(CLASS, handler); TopDownAnalyzer.processObject(context.semanticServices, traceAdapter, context.scope, context.scope.getContainingDeclaration(), expression.getObjectDeclaration()); - return ExpressionTypingUtils.checkType(result[0], expression, context); + return DataFlowUtils.checkType(result[0], expression, context); } @Override @@ -152,11 +152,11 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor { JetType expectedReturnType = JetStandardClasses.getReturnType(expectedType); if (JetStandardClasses.isUnit(expectedReturnType)) { functionDescriptor.setReturnType(expectedReturnType); - return ExpressionTypingUtils.checkType(JetStandardClasses.getFunctionType(Collections.emptyList(), effectiveReceiverType, parameterTypes, expectedReturnType), expression, context); + return DataFlowUtils.checkType(JetStandardClasses.getFunctionType(Collections.emptyList(), effectiveReceiverType, parameterTypes, expectedReturnType), expression, context); } } - return ExpressionTypingUtils.checkType(JetStandardClasses.getFunctionType(Collections.emptyList(), effectiveReceiverType, parameterTypes, safeReturnType), expression, context); + return DataFlowUtils.checkType(JetStandardClasses.getFunctionType(Collections.emptyList(), effectiveReceiverType, parameterTypes, safeReturnType), expression, context); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java index 8fb7c58659b..c2ca77a049e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java @@ -65,8 +65,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { JetExpression thenBranch = expression.getThen(); WritableScopeImpl thenScope = newWritableScopeImpl(context).setDebugName("Then scope"); - DataFlowInfo thenInfo = extractDataFlowInfoFromCondition(condition, true, thenScope, context); - DataFlowInfo elseInfo = extractDataFlowInfoFromCondition(condition, false, null, context); + DataFlowInfo thenInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, true, thenScope, context); + DataFlowInfo elseInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, null, context); if (elseBranch == null) { if (thenBranch != null) { @@ -75,7 +75,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { facade.setResultingDataFlowInfo(elseInfo); // resultScope = elseScope; } - return checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType); + return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType); } return null; } @@ -85,7 +85,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { facade.setResultingDataFlowInfo(thenInfo); // resultScope = thenScope; } - return checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType); + return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType); } JetType thenType = getTypeWithNewScopeAndDataFlowInfo(thenScope, thenBranch, thenInfo, contextWithExpectedType); JetType elseType = getTypeWithNewScopeAndDataFlowInfo(context.scope, elseBranch, elseInfo, contextWithExpectedType); @@ -121,13 +121,13 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { JetExpression body = expression.getBody(); if (body != null) { WritableScopeImpl scopeToExtend = newWritableScopeImpl(context).setDebugName("Scope extended in while's condition"); - DataFlowInfo conditionInfo = condition == null ? context.dataFlowInfo : extractDataFlowInfoFromCondition(condition, true, scopeToExtend, context); + DataFlowInfo conditionInfo = condition == null ? context.dataFlowInfo : DataFlowUtils.extractDataFlowInfoFromCondition(condition, true, scopeToExtend, context); getTypeWithNewScopeAndDataFlowInfo(scopeToExtend, body, conditionInfo, context); } if (!containsBreak(expression, context)) { - facade.setResultingDataFlowInfo(extractDataFlowInfoFromCondition(condition, false, null, context)); + facade.setResultingDataFlowInfo(DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, null, context)); } - return checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType); + return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType); } private boolean containsBreak(final JetLoopExpression loopExpression, final ExpressionTypingContext context) { @@ -178,9 +178,9 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { JetExpression condition = expression.getCondition(); checkCondition(conditionScope, condition, context); if (!containsBreak(expression, context)) { - facade.setResultingDataFlowInfo(extractDataFlowInfoFromCondition(condition, false, null, context)); + facade.setResultingDataFlowInfo(DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, null, context)); } - return checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType); + return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType); } @Override @@ -225,7 +225,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { facade.getType(body, context.replaceScope(loopScope)); } - return checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType); + return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType); } @Nullable @@ -372,7 +372,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { JetType type = facade.getType(thrownExpression, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceScope(context.scope)); // TODO : check that it inherits Throwable } - return checkType(JetStandardClasses.getNothingType(), expression, context); + return DataFlowUtils.checkType(JetStandardClasses.getNothingType(), expression, context); } @Override @@ -395,19 +395,19 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { context.trace.report(RETURN_TYPE_MISMATCH.on(expression, context.expectedReturnType)); } } - return checkType(JetStandardClasses.getNothingType(), expression, context); + return DataFlowUtils.checkType(JetStandardClasses.getNothingType(), expression, context); } @Override public JetType visitBreakExpression(JetBreakExpression expression, ExpressionTypingContext context) { context.labelResolver.recordLabel(expression, context); - return checkType(JetStandardClasses.getNothingType(), expression, context); + return DataFlowUtils.checkType(JetStandardClasses.getNothingType(), expression, context); } @Override public JetType visitContinueExpression(JetContinueExpression expression, ExpressionTypingContext context) { context.labelResolver.recordLabel(expression, context); - return checkType(JetStandardClasses.getNothingType(), expression, context); + return DataFlowUtils.checkType(JetStandardClasses.getNothingType(), expression, context); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/DataFlowUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/DataFlowUtils.java new file mode 100644 index 00000000000..f7980849808 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/DataFlowUtils.java @@ -0,0 +1,157 @@ +package org.jetbrains.jet.lang.types.expressions; + +import com.intellij.openapi.util.Ref; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.VariableDescriptor; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory; +import org.jetbrains.jet.lang.resolve.scopes.WritableScope; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.JetTypeChecker; +import org.jetbrains.jet.lang.types.TypeUtils; +import org.jetbrains.jet.lexer.JetTokens; + +import java.util.List; + +import static org.jetbrains.jet.lang.diagnostics.Errors.AUTOCAST_IMPOSSIBLE; +import static org.jetbrains.jet.lang.diagnostics.Errors.TYPE_MISMATCH; +import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST; + +/** + * @author abreslav + */ +public class DataFlowUtils { + @NotNull + public static DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, @Nullable final WritableScope scopeToExtend, final ExpressionTypingContext context) { + if (condition == null) return context.dataFlowInfo; + final Ref result = new Ref(context.dataFlowInfo); + condition.accept(new JetVisitorVoid() { + @Override + public void visitIsExpression(JetIsExpression expression) { + if (conditionValue && !expression.isNegated() || !conditionValue && expression.isNegated()) { + JetPattern pattern = expression.getPattern(); + result.set(context.patternsToDataFlowInfo.get(pattern)); + if (scopeToExtend != null) { + List descriptors = context.patternsToBoundVariableLists.get(pattern); + if (descriptors != null) { + for (VariableDescriptor variableDescriptor : descriptors) { + scopeToExtend.addVariableDescriptor(variableDescriptor); + } + } + } + } + } + + @Override + public void visitBinaryExpression(JetBinaryExpression expression) { + IElementType operationToken = expression.getOperationToken(); + if (operationToken == JetTokens.ANDAND || operationToken == JetTokens.OROR) { + WritableScope actualScopeToExtend; + if (operationToken == JetTokens.ANDAND) { + actualScopeToExtend = conditionValue ? scopeToExtend : null; + } + else { + actualScopeToExtend = conditionValue ? null : scopeToExtend; + } + + DataFlowInfo dataFlowInfo = extractDataFlowInfoFromCondition(expression.getLeft(), conditionValue, actualScopeToExtend, context); + JetExpression expressionRight = expression.getRight(); + if (expressionRight != null) { + DataFlowInfo rightInfo = extractDataFlowInfoFromCondition(expressionRight, conditionValue, actualScopeToExtend, context); + DataFlowInfo.CompositionOperator operator; + if (operationToken == JetTokens.ANDAND) { + operator = conditionValue ? DataFlowInfo.AND : DataFlowInfo.OR; + } + else { + operator = conditionValue ? DataFlowInfo.OR : DataFlowInfo.AND; + } + dataFlowInfo = operator.compose(dataFlowInfo, rightInfo); + } + result.set(dataFlowInfo); + } + else { + JetExpression left = expression.getLeft(); + JetExpression right = expression.getRight(); + if (right == null) return; + + JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left); + if (lhsType == null) return; + JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right); + if (rhsType == null) return; + + BindingContext bindingContext = context.trace.getBindingContext(); + DataFlowValue leftValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(left, lhsType, bindingContext); + DataFlowValue rightValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(right, rhsType, bindingContext); + + Boolean equals = null; + if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) { + equals = true; + } + else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) { + equals = false; + } + if (equals != null) { + if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue + result.set(context.dataFlowInfo.equate(leftValue, rightValue)); + } + else { + result.set(context.dataFlowInfo.disequate(leftValue, rightValue)); + } + + } + } + } + + @Override + public void visitUnaryExpression(JetUnaryExpression expression) { + IElementType operationTokenType = expression.getOperationSign().getReferencedNameElementType(); + if (operationTokenType == JetTokens.EXCL) { + JetExpression baseExpression = expression.getBaseExpression(); + if (baseExpression != null) { + result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, scopeToExtend, context)); + } + } + } + + @Override + public void visitParenthesizedExpression(JetParenthesizedExpression expression) { + JetExpression body = expression.getExpression(); + if (body != null) { + body.accept(this); + } + } + }); + if (result.get() == null) { + return context.dataFlowInfo; + } + return result.get(); + } + + @Nullable + public static JetType checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context) { + if (expressionType == null || context.expectedType == null || context.expectedType == TypeUtils.NO_EXPECTED_TYPE || + context.semanticServices.getTypeChecker().isSubtypeOf(expressionType, context.expectedType)) { + return expressionType; + } + + DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(expression, expressionType, context.trace.getBindingContext()); + for (JetType possibleType : context.dataFlowInfo.getPossibleTypes(dataFlowValue)) { + if (JetTypeChecker.INSTANCE.isSubtypeOf(possibleType, context.expectedType)) { + if (dataFlowValue.isStableIdentifier()) { + context.trace.record(AUTOCAST, expression, possibleType); + } + else { + context.trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText())); + } + return possibleType; + } + } + context.trace.report(TYPE_MISMATCH.on(expression, context.expectedType, expressionType)); + return expressionType; + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java index 0ea1a9c113a..aacb5b29402 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java @@ -128,7 +128,7 @@ public class ExpressionTypingServices { /*package*/ JetType getBlockReturnedType(@NotNull JetScope outerScope, @NotNull JetBlockExpression expression, @NotNull CoercionStrategy coercionStrategyForLastExpression, ExpressionTypingContext context) { List block = expression.getStatements(); if (block.isEmpty()) { - return ExpressionTypingUtils.checkType(JetStandardClasses.getUnitType(), expression, context); + return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, context); } DeclarationDescriptor containingDescriptor = outerScope.getContainingDeclaration(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java index 7ce0dce8bc2..b95fe53a31c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java @@ -1,6 +1,5 @@ package org.jetbrains.jet.lang.types.expressions; -import com.intellij.openapi.util.Ref; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; @@ -10,25 +9,16 @@ import org.jetbrains.jet.lang.JetSemanticServices; import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.VariableDescriptor; -import org.jetbrains.jet.lang.psi.*; -import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; import org.jetbrains.jet.lang.resolve.TraceBasedRedeclarationHandler; -import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; -import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory; -import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetType; -import org.jetbrains.jet.lang.types.JetTypeChecker; -import org.jetbrains.jet.lang.types.TypeUtils; -import org.jetbrains.jet.lexer.JetTokens; -import java.util.List; - -import static org.jetbrains.jet.lang.diagnostics.Errors.*; -import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST; +import static org.jetbrains.jet.lang.diagnostics.Errors.RESULT_TYPE_MISMATCH; import static org.jetbrains.jet.lang.resolve.BindingContext.MUST_BE_WRAPPED_IN_A_REF; /** @@ -100,166 +90,6 @@ public class ExpressionTypingUtils { } } - @NotNull - public static DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, @Nullable final WritableScope scopeToExtend, final ExpressionTypingContext context) { - if (condition == null) return context.dataFlowInfo; - final Ref result = new Ref(context.dataFlowInfo); - condition.accept(new JetVisitorVoid() { - @Override - public void visitIsExpression(JetIsExpression expression) { - if (conditionValue && !expression.isNegated() || !conditionValue && expression.isNegated()) { - JetPattern pattern = expression.getPattern(); - result.set(context.patternsToDataFlowInfo.get(pattern)); - if (scopeToExtend != null) { - List descriptors = context.patternsToBoundVariableLists.get(pattern); - if (descriptors != null) { - for (VariableDescriptor variableDescriptor : descriptors) { - scopeToExtend.addVariableDescriptor(variableDescriptor); - } - } - } - } - } - - @Override - public void visitBinaryExpression(JetBinaryExpression expression) { - IElementType operationToken = expression.getOperationToken(); - if (operationToken == JetTokens.ANDAND || operationToken == JetTokens.OROR) { - WritableScope actualScopeToExtend; - if (operationToken == JetTokens.ANDAND) { - actualScopeToExtend = conditionValue ? scopeToExtend : null; - } - else { - actualScopeToExtend = conditionValue ? null : scopeToExtend; - } - - DataFlowInfo dataFlowInfo = extractDataFlowInfoFromCondition(expression.getLeft(), conditionValue, actualScopeToExtend, context); - JetExpression expressionRight = expression.getRight(); - if (expressionRight != null) { - DataFlowInfo rightInfo = extractDataFlowInfoFromCondition(expressionRight, conditionValue, actualScopeToExtend, context); - DataFlowInfo.CompositionOperator operator; - if (operationToken == JetTokens.ANDAND) { - operator = conditionValue ? DataFlowInfo.AND : DataFlowInfo.OR; - } - else { - operator = conditionValue ? DataFlowInfo.OR : DataFlowInfo.AND; - } - dataFlowInfo = operator.compose(dataFlowInfo, rightInfo); - } - result.set(dataFlowInfo); - } - else { - JetExpression left = expression.getLeft(); - JetExpression right = expression.getRight(); - if (right == null) return; - - JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left); - if (lhsType == null) return; - JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right); - if (rhsType == null) return; - - BindingContext bindingContext = context.trace.getBindingContext(); - DataFlowValue leftValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(left, lhsType, bindingContext); - DataFlowValue rightValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(right, rhsType, bindingContext); - - Boolean equals = null; - if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) { - equals = true; - } - else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) { - equals = false; - } - if (equals != null) { - if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue - result.set(context.dataFlowInfo.equate(leftValue, rightValue)); - } - else { - result.set(context.dataFlowInfo.disequate(leftValue, rightValue)); - } - - } - -// if (!(left instanceof JetSimpleNameExpression)) { -// JetExpression tmp = left; -// left = right; -// right = tmp; -// -// if (!(left instanceof JetSimpleNameExpression)) { -// return; -// } -// } -// -// VariableDescriptor variableDescriptor = DataFlowValueFactory.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), left); -// if (variableDescriptor == null) return; -// -// // TODO : validate that DF makes sense for this variable: local, val, internal w/backing field, etc -// -// // Comparison to a non-null expression -// JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right); -// if (rhsType != null && !rhsType.isNullable()) { -// extendDataFlowWithNullComparison(operationToken, variableDescriptor, !conditionValue); -// return; -// } -// -// VariableDescriptor rightVariable = DataFlowValueFactory.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), right); -// if (rightVariable != null) { -// JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left); -// if (lhsType != null && !lhsType.isNullable()) { -// extendDataFlowWithNullComparison(operationToken, rightVariable, !conditionValue); -// return; -// } -// } -// -// // Comparison to 'null' -// if (!(right instanceof JetConstantExpression)) { -// return; -// } -// JetConstantExpression constantExpression = (JetConstantExpression) right; -// if (constantExpression.getNode().getElementType() != JetNodeTypes.NULL) { -// return; -// } -// -// extendDataFlowWithNullComparison(operationToken, variableDescriptor, conditionValue); - } - } - -// private void extendDataFlowWithNullComparison(IElementType operationToken, @NotNull VariableDescriptor variableDescriptor, boolean equalsToNull) { -// boolean equality; -// if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) { -//// result[0] = context.dataFlowInfo.establishEqualityToNull(variableDescriptor, !equalsToNull); -// equality = true; -// } -// else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) { -//// result[0] = context.dataFlowInfo.establishEqualityToNull(variableDescriptor, equalsToNull); -// equality = false; -// } -// } - - @Override - public void visitUnaryExpression(JetUnaryExpression expression) { - IElementType operationTokenType = expression.getOperationSign().getReferencedNameElementType(); - if (operationTokenType == JetTokens.EXCL) { - JetExpression baseExpression = expression.getBaseExpression(); - if (baseExpression != null) { - result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, scopeToExtend, context)); - } - } - } - - @Override - public void visitParenthesizedExpression(JetParenthesizedExpression expression) { - JetExpression body = expression.getExpression(); - if (body != null) { - body.accept(this); - } - } - }); - if (result.get() == null) { - return context.dataFlowInfo; - } - return result.get(); - } - public static boolean isTypeFlexible(@Nullable JetExpression expression) { if (expression == null) return false; @@ -269,29 +99,6 @@ public class ExpressionTypingUtils { ).contains(expression.getNode().getElementType()); } - @Nullable - public static JetType checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context) { - if (expressionType == null || context.expectedType == null || context.expectedType == TypeUtils.NO_EXPECTED_TYPE || - context.semanticServices.getTypeChecker().isSubtypeOf(expressionType, context.expectedType)) { - return expressionType; - } - - DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(expression, expressionType, context.trace.getBindingContext()); - for (JetType possibleType : context.dataFlowInfo.getPossibleTypes(dataFlowValue)) { - if (JetTypeChecker.INSTANCE.isSubtypeOf(possibleType, context.expectedType)) { - if (dataFlowValue.isStableIdentifier()) { - context.trace.record(AUTOCAST, expression, possibleType); - } - else { - context.trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText())); - } - return possibleType; - } - } - context.trace.report(TYPE_MISMATCH.on(expression, context.expectedType, expressionType)); - return expressionType; - } - public static void checkWrappingInRef(JetExpression expression, ExpressionTypingContext context) { if (!(expression instanceof JetSimpleNameExpression)) return; JetSimpleNameExpression simpleName = (JetSimpleNameExpression) expression; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorWithWritableScope.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorWithWritableScope.java index acbd1271b57..844f980e8b5 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorWithWritableScope.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorWithWritableScope.java @@ -164,7 +164,7 @@ public class ExpressionTypingVisitorWithWritableScope extends BasicExpressionTyp "set", receiver); if (functionDescriptor == null) return null; context.trace.record(REFERENCE_TARGET, operationSign, functionDescriptor); - return ExpressionTypingUtils.checkType(functionDescriptor.getReturnType(), arrayAccessExpression, context); + return DataFlowUtils.checkType(functionDescriptor.getReturnType(), arrayAccessExpression, context); } @Override diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java index 5d1783f026b..970198fe48d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java @@ -44,7 +44,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { context.patternsToDataFlowInfo.put(pattern, newDataFlowInfo); context.patternsToBoundVariableLists.put(pattern, scopeToExtend.getDeclaredVariables()); } - return ExpressionTypingUtils.checkType(context.semanticServices.getStandardLibrary().getBooleanType(), expression, contextWithExpectedType); + return DataFlowUtils.checkType(context.semanticServices.getStandardLibrary().getBooleanType(), expression, contextWithExpectedType); } @Override diff --git a/idea/testData/checkerWithErrorTypes/quick/nullabilityAndAutoCasts/NullableNothingIsExactlyNull.jet b/idea/testData/checkerWithErrorTypes/quick/nullabilityAndAutoCasts/NullableNothingIsExactlyNull.jet new file mode 100644 index 00000000000..3c52c45b1b9 --- /dev/null +++ b/idea/testData/checkerWithErrorTypes/quick/nullabilityAndAutoCasts/NullableNothingIsExactlyNull.jet @@ -0,0 +1,8 @@ +fun test() { + val out : Int? = null + val x : Nothing? = null + if (out != x) + out.plus(1) + if (out == x) return + out.plus(1) +}