Nullable Nothing is exactly null

This commit is contained in:
Andrey Breslav
2011-10-19 12:46:32 +04:00
parent 54646ac8dd
commit e262d94cc1
12 changed files with 223 additions and 243 deletions
@@ -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<Object, Boolean> result = getIdForStableIdentifier(expression, bindingContext, false);
return new DataFlowValue(result.first == null ? expression : result.first, type, result.second, getImmanentNullability(type));
}
@@ -39,12 +39,12 @@ public class JetStandardClasses {
@Override
public Iterator<JetType> 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) {
@@ -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;
@@ -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<JetType> 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
@@ -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.<AnnotationDescriptor>emptyList(), effectiveReceiverType, parameterTypes, expectedReturnType), expression, context);
return DataFlowUtils.checkType(JetStandardClasses.getFunctionType(Collections.<AnnotationDescriptor>emptyList(), effectiveReceiverType, parameterTypes, expectedReturnType), expression, context);
}
}
return ExpressionTypingUtils.checkType(JetStandardClasses.getFunctionType(Collections.<AnnotationDescriptor>emptyList(), effectiveReceiverType, parameterTypes, safeReturnType), expression, context);
return DataFlowUtils.checkType(JetStandardClasses.getFunctionType(Collections.<AnnotationDescriptor>emptyList(), effectiveReceiverType, parameterTypes, safeReturnType), expression, context);
}
}
@@ -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);
}
@@ -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<DataFlowInfo> result = new Ref<DataFlowInfo>(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<VariableDescriptor> 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;
}
}
@@ -128,7 +128,7 @@ public class ExpressionTypingServices {
/*package*/ JetType getBlockReturnedType(@NotNull JetScope outerScope, @NotNull JetBlockExpression expression, @NotNull CoercionStrategy coercionStrategyForLastExpression, ExpressionTypingContext context) {
List<JetElement> block = expression.getStatements();
if (block.isEmpty()) {
return ExpressionTypingUtils.checkType(JetStandardClasses.getUnitType(), expression, context);
return DataFlowUtils.checkType(JetStandardClasses.getUnitType(), expression, context);
}
DeclarationDescriptor containingDescriptor = outerScope.getContainingDeclaration();
@@ -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<DataFlowInfo> result = new Ref<DataFlowInfo>(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<VariableDescriptor> 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;
@@ -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
@@ -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
@@ -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)
}