diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java index 953265a3b23..ca10fcce842 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java @@ -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 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 unmappedArguments) { for (ValueArgument valueArgument : unmappedArguments) { JetExpression argumentExpression = valueArgument.getArgumentExpression(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index e4eeef77e91..a844caeba16 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -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 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 diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpressionCompareToConvention.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpressionCompareToConvention.kt new file mode 100644 index 00000000000..637522393d3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpressionCompareToConvention.kt @@ -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 +} diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpressionIdentifier.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpressionIdentifier.kt new file mode 100644 index 00000000000..6b1518c79c9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpressionIdentifier.kt @@ -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 +} diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpressionPlusConvention.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpressionPlusConvention.kt new file mode 100644 index 00000000000..fdb09b3ecb3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpressionPlusConvention.kt @@ -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 +} diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 0a9c3355b80..b02a5309191 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -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");