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.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();
|
||||
|
||||
+37
-11
@@ -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
|
||||
|
||||
+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");
|
||||
}
|
||||
|
||||
@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");
|
||||
|
||||
Reference in New Issue
Block a user