Working on bindings in ifs

This commit is contained in:
Andrey Breslav
2011-06-13 14:33:32 +04:00
parent c2aceefe76
commit 3cacb512a3
6 changed files with 128 additions and 48 deletions
@@ -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<VariableDescriptor> getDeclaredVariables() {
List<VariableDescriptor> 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();
}
}
@@ -155,6 +155,7 @@ public class ErrorUtils {
return result;
}
@NotNull
public static JetType createErrorType(String debugMessage) {
return createErrorType(debugMessage, ERROR_SCOPE);
}
@@ -72,6 +72,8 @@ public class JetTypeInferrer {
private final TypeResolver typeResolver;
private final ClassDescriptorResolver classDescriptorResolver;
private final JetFlowInformationProvider flowInformationProvider;
private final Map<JetPattern, DataFlowInfo> patternsToDataFlowInfo = Maps.newHashMap();
private final Map<JetPattern, List<VariableDescriptor>> 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<JetType> 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("<Unknown>");
// }
// 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 {
+1 -1
View File
@@ -1,6 +1,6 @@
fun test() {
if (1 is Int) {
if (<warning>1 is Boolean</warning>) {
if (1 is <error>Boolean</error>) {
}
}
+10
View File
@@ -75,4 +75,14 @@ fun f12(a : A?) {
is val c is C => <info descr="Automatically cast to C">a</info>.bar()
else => a?.foo()
}
if (a is val b) {
a?.<error>bar</error>()
b?.foo()
}
if (a is val b is B) {
b.foo()
<info descr="Automatically cast to B">a</info>.bar()
<info descr="Automatically cast to B">b</info>.bar()
}
}
@@ -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
}