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:
@@ -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.ResolutionTask;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.tasks.TaskPrioritizer;
|
import org.jetbrains.jet.lang.resolve.calls.tasks.TaskPrioritizer;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.tasks.TracingStrategy;
|
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.resolve.scopes.receivers.ReceiverValue;
|
||||||
import org.jetbrains.jet.lang.types.*;
|
import org.jetbrains.jet.lang.types.*;
|
||||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
|
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);
|
JetTypeInfo typeInfo = expressionTypingServices.getTypeInfo(context.scope, expression, expectedType, dataFlowInfo, trace);
|
||||||
JetType type = typeInfo.getType();
|
JetType type = typeInfo.getType();
|
||||||
argumentTypes.add(type);
|
|
||||||
dataFlowInfo = dataFlowInfo.and(typeInfo.getDataFlowInfo());
|
dataFlowInfo = dataFlowInfo.and(typeInfo.getDataFlowInfo());
|
||||||
|
|
||||||
if (type == null || ErrorUtils.isErrorType(type)) {
|
if (type == null || ErrorUtils.isErrorType(type)) {
|
||||||
candidateCall.argumentHasNoType();
|
candidateCall.argumentHasNoType();
|
||||||
|
argumentTypes.add(type);
|
||||||
}
|
}
|
||||||
else if (expectedType != NO_EXPECTED_TYPE && !typeChecker.isSubtypeOf(type, expectedType)) {
|
else {
|
||||||
// VariableDescriptor variableDescriptor = AutoCastUtils.getVariableDescriptorFromSimpleName(temporaryTrace.getBindingContext(), argument);
|
JetType resultingType;
|
||||||
// if (variableDescriptor != null) {
|
if (expectedType == NO_EXPECTED_TYPE || typeChecker.isSubtypeOf(type, expectedType)) {
|
||||||
// JetType autoCastType = null;
|
resultingType = type;
|
||||||
// for (JetType possibleType : dataFlowInfo.getPossibleTypesForVariable(variableDescriptor)) {
|
}
|
||||||
// if (semanticServices.getTypeChecker().isSubtypeOf(type, parameterType)) {
|
else {
|
||||||
// autoCastType = possibleType;
|
resultingType = autocastValueArgumentTypeIfPossible(expression, expectedType, type, trace, dataFlowInfo);
|
||||||
// break;
|
if (resultingType == null) {
|
||||||
// }
|
resultingType = type;
|
||||||
// }
|
resultStatus = OTHER_ERROR;
|
||||||
// if (autoCastType != null) {
|
}
|
||||||
// if (AutoCastUtils.isStableVariable(variableDescriptor)) {
|
}
|
||||||
// temporaryTrace.record(AUTOCAST, argument, autoCastType);
|
|
||||||
// }
|
argumentTypes.add(resultingType);
|
||||||
// else {
|
|
||||||
// temporaryTrace.report(AUTOCAST_IMPOSSIBLE.on(argument, autoCastType, variableDescriptor));
|
|
||||||
// resultStatus = false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
resultStatus = OTHER_ERROR;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new ValueArgumentsCheckingResult(resultStatus, argumentTypes);
|
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) {
|
private void checkUnmappedArgumentTypes(ResolutionContext context, Set<ValueArgument> unmappedArguments) {
|
||||||
for (ValueArgument valueArgument : unmappedArguments) {
|
for (ValueArgument valueArgument : unmappedArguments) {
|
||||||
JetExpression argumentExpression = valueArgument.getArgumentExpression();
|
JetExpression argumentExpression = valueArgument.getArgumentExpression();
|
||||||
|
|||||||
+37
-11
@@ -1058,17 +1058,23 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
|
|
||||||
JetExpression left = expression.getLeft();
|
JetExpression left = expression.getLeft();
|
||||||
JetExpression right = expression.getRight();
|
JetExpression right = expression.getRight();
|
||||||
|
IElementType operationType = operationSign.getReferencedNameElementType();
|
||||||
|
|
||||||
JetType result = null;
|
JetType result = null;
|
||||||
IElementType operationType = operationSign.getReferencedNameElementType();
|
DataFlowInfo dataFlowInfo = context.dataFlowInfo;
|
||||||
if (operationType == JetTokens.IDENTIFIER) {
|
if (operationType == JetTokens.IDENTIFIER) {
|
||||||
Name referencedName = operationSign.getReferencedNameAsName();
|
Name referencedName = operationSign.getReferencedNameAsName();
|
||||||
if (referencedName != null) {
|
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)) {
|
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) {
|
else if (operationType == JetTokens.EQ) {
|
||||||
result = visitAssignment(expression, contextWithExpectedType);
|
result = visitAssignment(expression, contextWithExpectedType);
|
||||||
@@ -1077,7 +1083,9 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
result = visitAssignmentOperation(expression, contextWithExpectedType);
|
result = visitAssignmentOperation(expression, contextWithExpectedType);
|
||||||
}
|
}
|
||||||
else if (OperatorConventions.COMPARISON_OPERATIONS.contains(operationType)) {
|
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)) {
|
if (compareToReturnType != null && !ErrorUtils.isErrorType(compareToReturnType)) {
|
||||||
TypeConstructor constructor = compareToReturnType.getConstructor();
|
TypeConstructor constructor = compareToReturnType.getConstructor();
|
||||||
KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
|
KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
|
||||||
@@ -1128,7 +1136,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
else if (OperatorConventions.IN_OPERATIONS.contains(operationType)) {
|
else if (OperatorConventions.IN_OPERATIONS.contains(operationType)) {
|
||||||
if (right == null) {
|
if (right == null) {
|
||||||
result = ErrorUtils.createErrorType("No right argument"); // TODO
|
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);
|
checkInExpression(expression, expression.getOperationReference(), expression.getLeft(), expression.getRight(), context);
|
||||||
result = booleanType;
|
result = booleanType;
|
||||||
@@ -1164,7 +1172,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
DataFlowUtils.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType);
|
DataFlowUtils.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType);
|
||||||
return JetTypeInfo.create(TypeUtils.makeNullableAsSpecified(
|
return JetTypeInfo.create(TypeUtils.makeNullableAsSpecified(
|
||||||
CommonSupertypes.commonSupertype(Arrays.asList(leftType, rightType)), rightType.isNullable()),
|
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"));
|
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) {
|
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);
|
return JetTypeInfo.create(type, context.dataFlowInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@NotNull
|
||||||
public JetType getTypeForBinaryCall(JetScope scope, Name name, ExpressionTypingContext context, JetBinaryExpression binaryExpression) {
|
public JetTypeInfo getTypeInfoForBinaryCall(
|
||||||
ExpressionReceiver receiver = safeGetExpressionReceiver(facade, binaryExpression.getLeft(), context.replaceScope(scope));
|
JetScope scope,
|
||||||
return OverloadResolutionResultsUtil.getResultType(getResolutionResultsForBinaryCall(scope, name, context, binaryExpression, receiver));
|
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
|
@NotNull
|
||||||
|
|||||||
+16
@@ -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
|
||||||
|
}
|
||||||
+11
@@ -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
|
||||||
|
}
|
||||||
+16
@@ -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");
|
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")
|
@TestMetadata("DeepIf.kt")
|
||||||
public void testDeepIf() throws Exception {
|
public void testDeepIf() throws Exception {
|
||||||
doTest("compiler/testData/diagnostics/tests/dataFlowInfoTraversal/DeepIf.kt");
|
doTest("compiler/testData/diagnostics/tests/dataFlowInfoTraversal/DeepIf.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user