Retain data flow info after binary calls

Make autocasts in CandidateResolver when checking value argument types

 #KT-2825 In Progress
This commit is contained in:
Alexander Udalov
2012-11-12 17:37:05 +04:00
parent a71ea5c6db
commit 1fbe9b9a31
6 changed files with 131 additions and 34 deletions
@@ -38,6 +38,7 @@ import org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus;
import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionTask;
import org.jetbrains.jet.lang.resolve.calls.tasks.TaskPrioritizer;
import org.jetbrains.jet.lang.resolve.calls.tasks.TracingStrategy;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
@@ -417,39 +418,51 @@ public class CandidateResolver {
}
JetTypeInfo typeInfo = expressionTypingServices.getTypeInfo(context.scope, expression, expectedType, dataFlowInfo, trace);
JetType type = typeInfo.getType();
argumentTypes.add(type);
dataFlowInfo = dataFlowInfo.and(typeInfo.getDataFlowInfo());
if (type == null || ErrorUtils.isErrorType(type)) {
candidateCall.argumentHasNoType();
argumentTypes.add(type);
}
else if (expectedType != NO_EXPECTED_TYPE && !typeChecker.isSubtypeOf(type, expectedType)) {
// VariableDescriptor variableDescriptor = AutoCastUtils.getVariableDescriptorFromSimpleName(temporaryTrace.getBindingContext(), argument);
// if (variableDescriptor != null) {
// JetType autoCastType = null;
// for (JetType possibleType : dataFlowInfo.getPossibleTypesForVariable(variableDescriptor)) {
// if (semanticServices.getTypeChecker().isSubtypeOf(type, parameterType)) {
// autoCastType = possibleType;
// break;
// }
// }
// if (autoCastType != null) {
// if (AutoCastUtils.isStableVariable(variableDescriptor)) {
// temporaryTrace.record(AUTOCAST, argument, autoCastType);
// }
// else {
// temporaryTrace.report(AUTOCAST_IMPOSSIBLE.on(argument, autoCastType, variableDescriptor));
// resultStatus = false;
// }
// }
// }
// else {
resultStatus = OTHER_ERROR;
else {
JetType resultingType;
if (expectedType == NO_EXPECTED_TYPE || typeChecker.isSubtypeOf(type, expectedType)) {
resultingType = type;
}
else {
resultingType = autocastValueArgumentTypeIfPossible(expression, expectedType, type, trace, dataFlowInfo);
if (resultingType == null) {
resultingType = type;
resultStatus = OTHER_ERROR;
}
}
argumentTypes.add(resultingType);
}
}
}
return new ValueArgumentsCheckingResult(resultStatus, argumentTypes);
}
@Nullable
private JetType autocastValueArgumentTypeIfPossible(
@NotNull JetExpression expression,
@NotNull JetType expectedType,
@NotNull JetType actualType,
@NotNull BindingTrace trace,
@NotNull DataFlowInfo dataFlowInfo
) {
ExpressionReceiver receiverToCast = new ExpressionReceiver(expression, actualType);
List<ReceiverValue> variants = AutoCastUtils.getAutoCastVariants(trace.getBindingContext(), dataFlowInfo, receiverToCast);
for (ReceiverValue receiverValue : variants) {
JetType possibleType = receiverValue.getType();
if (typeChecker.isSubtypeOf(possibleType, expectedType)) {
return possibleType;
}
}
return null;
}
private void checkUnmappedArgumentTypes(ResolutionContext context, Set<ValueArgument> unmappedArguments) {
for (ValueArgument valueArgument : unmappedArguments) {
JetExpression argumentExpression = valueArgument.getArgumentExpression();
@@ -1058,17 +1058,23 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
JetExpression left = expression.getLeft();
JetExpression right = expression.getRight();
IElementType operationType = operationSign.getReferencedNameElementType();
JetType result = null;
IElementType operationType = operationSign.getReferencedNameElementType();
DataFlowInfo dataFlowInfo = context.dataFlowInfo;
if (operationType == JetTokens.IDENTIFIER) {
Name referencedName = operationSign.getReferencedNameAsName();
if (referencedName != null) {
result = getTypeForBinaryCall(context.scope, referencedName, context, expression);
JetTypeInfo typeInfo = getTypeInfoForBinaryCall(context.scope, referencedName, context, expression);
result = typeInfo.getType();
dataFlowInfo = typeInfo.getDataFlowInfo();
}
}
else if (OperatorConventions.BINARY_OPERATION_NAMES.containsKey(operationType)) {
result = getTypeForBinaryCall(context.scope, OperatorConventions.BINARY_OPERATION_NAMES.get(operationType), context, expression);
JetTypeInfo typeInfo = getTypeInfoForBinaryCall(context.scope, OperatorConventions.BINARY_OPERATION_NAMES.get(operationType),
context, expression);
result = typeInfo.getType();
dataFlowInfo = typeInfo.getDataFlowInfo();
}
else if (operationType == JetTokens.EQ) {
result = visitAssignment(expression, contextWithExpectedType);
@@ -1077,7 +1083,9 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
result = visitAssignmentOperation(expression, contextWithExpectedType);
}
else if (OperatorConventions.COMPARISON_OPERATIONS.contains(operationType)) {
JetType compareToReturnType = getTypeForBinaryCall(context.scope, Name.identifier("compareTo"), context, expression);
JetTypeInfo typeInfo = getTypeInfoForBinaryCall(context.scope, Name.identifier("compareTo"), context, expression);
dataFlowInfo = typeInfo.getDataFlowInfo();
JetType compareToReturnType = typeInfo.getType();
if (compareToReturnType != null && !ErrorUtils.isErrorType(compareToReturnType)) {
TypeConstructor constructor = compareToReturnType.getConstructor();
KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
@@ -1128,7 +1136,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
else if (OperatorConventions.IN_OPERATIONS.contains(operationType)) {
if (right == null) {
result = ErrorUtils.createErrorType("No right argument"); // TODO
return JetTypeInfo.create(null, context.dataFlowInfo);
return JetTypeInfo.create(null, dataFlowInfo);
}
checkInExpression(expression, expression.getOperationReference(), expression.getLeft(), expression.getRight(), context);
result = booleanType;
@@ -1164,7 +1172,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
DataFlowUtils.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType);
return JetTypeInfo.create(TypeUtils.makeNullableAsSpecified(
CommonSupertypes.commonSupertype(Arrays.asList(leftType, rightType)), rightType.isNullable()),
context.dataFlowInfo);
dataFlowInfo);
}
}
}
@@ -1172,7 +1180,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
context.trace.report(UNSUPPORTED.on(operationSign, "Unknown operation"));
}
}
return DataFlowUtils.checkType(result, expression, contextWithExpectedType, context.dataFlowInfo);
return DataFlowUtils.checkType(result, expression, contextWithExpectedType, dataFlowInfo);
}
public boolean checkInExpression(JetElement callElement, @NotNull JetSimpleNameExpression operationSign, @Nullable JetExpression left, @NotNull JetExpression right, ExpressionTypingContext context) {
@@ -1255,10 +1263,28 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return JetTypeInfo.create(type, context.dataFlowInfo);
}
@Nullable
public JetType getTypeForBinaryCall(JetScope scope, Name name, ExpressionTypingContext context, JetBinaryExpression binaryExpression) {
ExpressionReceiver receiver = safeGetExpressionReceiver(facade, binaryExpression.getLeft(), context.replaceScope(scope));
return OverloadResolutionResultsUtil.getResultType(getResolutionResultsForBinaryCall(scope, name, context, binaryExpression, receiver));
@NotNull
public JetTypeInfo getTypeInfoForBinaryCall(
JetScope scope,
Name name,
ExpressionTypingContext contextWithOldScope,
JetBinaryExpression binaryExpression
) {
ExpressionTypingContext context = contextWithOldScope.replaceScope(scope);
JetExpression left = binaryExpression.getLeft();
DataFlowInfo dataFlowInfo = facade.getTypeInfo(left, context).getDataFlowInfo();
ExpressionReceiver receiver = safeGetExpressionReceiver(facade, left, context);
ExpressionTypingContext contextWithDataFlow = context.replaceDataFlowInfo(dataFlowInfo);
OverloadResolutionResults<FunctionDescriptor> resolutionResults =
getResolutionResultsForBinaryCall(scope, name, contextWithDataFlow, binaryExpression, receiver);
JetExpression right = binaryExpression.getRight();
if (right != null) {
dataFlowInfo = facade.getTypeInfo(right, contextWithDataFlow).getDataFlowInfo();
}
return JetTypeInfo.create(OverloadResolutionResultsUtil.getResultType(resolutionResults), dataFlowInfo);
}
@NotNull
@@ -0,0 +1,16 @@
trait A
trait B : A
fun B.compareTo(b: B) = if (this == b) 0 else 1
fun foo(a: A): Boolean {
val result = (a as B) < a
a : B
return result
}
fun bar(a: A, b: B): Boolean {
val result = b < (a as B)
a : B
return result
}
@@ -0,0 +1,11 @@
fun foo(a: Number): Int {
val result = (a as Int) compareTo a
a : Int
return result
}
fun bar(a: Number): Int {
val result = 42 compareTo (a as Int)
a : Int
return result
}
@@ -0,0 +1,16 @@
trait A
trait B : A
fun B.plus(b: B) = if (this == b) b else this
fun foo(a: A): B {
val result = (a as B) + a
a : B
return result
}
fun bar(a: A, b: B): B {
val result = b + (a as B)
a : B
return result
}
@@ -1146,6 +1146,21 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
doTest("compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpression.kt");
}
@TestMetadata("BinaryExpressionCompareToConvention.kt")
public void testBinaryExpressionCompareToConvention() throws Exception {
doTest("compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpressionCompareToConvention.kt");
}
@TestMetadata("BinaryExpressionIdentifier.kt")
public void testBinaryExpressionIdentifier() throws Exception {
doTest("compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpressionIdentifier.kt");
}
@TestMetadata("BinaryExpressionPlusConvention.kt")
public void testBinaryExpressionPlusConvention() throws Exception {
doTest("compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpressionPlusConvention.kt");
}
@TestMetadata("DeepIf.kt")
public void testDeepIf() throws Exception {
doTest("compiler/testData/diagnostics/tests/dataFlowInfoTraversal/DeepIf.kt");