diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index 382a2debb12..eec1996b9e3 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -22,6 +22,7 @@ import java.util.*; import static org.jetbrains.jet.lang.diagnostics.Errors.*; import static org.jetbrains.jet.lang.resolve.BindingContext.*; +import static org.jetbrains.jet.lang.resolve.calls.ResolutionStatus.*; import static org.jetbrains.jet.lang.resolve.calls.ResolvedCall.MAP_TO_CANDIDATE; import static org.jetbrains.jet.lang.resolve.calls.ResolvedCall.MAP_TO_RESULT; import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor.NO_RECEIVER; @@ -345,9 +346,6 @@ public class CallResolver { @NotNull private OverloadResolutionResults performResolution(@NotNull BindingTrace trace, @NotNull JetScope scope, @NotNull JetType expectedType, @NotNull ResolutionTask task, @NotNull TracingStrategy tracing) { - Set> successfulCandidates = Sets.newLinkedHashSet(); - Set> failedCandidates = Sets.newLinkedHashSet(); - for (ResolvedCall candidateCall : task.getCandidates()) { D candidate = candidateCall.getCandidateDescriptor(); TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(trace); @@ -356,14 +354,14 @@ public class CallResolver { tracing.bindReference(temporaryTrace, candidateCall); if (ErrorUtils.isError(candidate)) { - successfulCandidates.add(candidateCall.setResultingDescriptor(candidate)); + candidateCall.setStatus(SUCCESS); checkTypesWithNoCallee(temporaryTrace, scope, task.getCall()); continue; } boolean errorInArgumentMapping = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(task, tracing, candidateCall); if (errorInArgumentMapping) { - failedCandidates.add(candidateCall); + candidateCall.setStatus(OTHER_ERROR); checkTypesWithNoCallee(temporaryTrace, scope, task.getCall()); continue; } @@ -415,20 +413,16 @@ public class CallResolver { D substitute = (D) candidate.substitute(solution.getSubstitutor()); assert substitute != null; replaceValueParametersWithSubstitutedOnes(candidateCall, substitute); - successfulCandidates.add(candidateCall.setResultingDescriptor(substitute)); + candidateCall.setResultingDescriptor(substitute); + candidateCall.setStatus(SUCCESS); } else { tracing.typeInferenceFailed(temporaryTrace); - failedCandidates.add(candidateCall); + candidateCall.setStatus(OTHER_ERROR); } } else { - if (checkAllValueArguments(scope, tracing, task, candidateCall)) { - successfulCandidates.add(candidateCall.setResultingDescriptor(candidate)); - } - else { - failedCandidates.add(candidateCall); - } + candidateCall.setStatus(checkAllValueArguments(scope, tracing, task, candidateCall)); } } else { @@ -459,15 +453,10 @@ public class CallResolver { candidateCall.setResultingDescriptor(substitutedDescriptor); replaceValueParametersWithSubstitutedOnes(candidateCall, substitutedDescriptor); - if (checkAllValueArguments(scope, tracing, task, candidateCall)) { - successfulCandidates.add(candidateCall); - } - else { - failedCandidates.add(candidateCall); - } + candidateCall.setStatus(checkAllValueArguments(scope, tracing, task, candidateCall)); } else { - failedCandidates.add(candidateCall); + candidateCall.setStatus(OTHER_ERROR); // tracing.reportWrongTypeArguments(temporaryTrace, "Number of type arguments does not match " + DescriptorRenderer.TEXT.render(candidate)); tracing.wrongNumberOfTypeArguments(temporaryTrace, expectedTypeArgumentCount); } @@ -479,6 +468,19 @@ public class CallResolver { recordAutoCastIfNecessary(candidateCall.getThisObject(), candidateCall.getTrace()); } + Set> successfulCandidates = Sets.newLinkedHashSet(); + Set> failedCandidates = Sets.newLinkedHashSet(); + for (ResolvedCall candidateCall : task.getCandidates()) { + ResolutionStatus status = candidateCall.getStatus(); + if (status.isSuccess()) { + successfulCandidates.add(candidateCall); + } + else { + assert status != UNKNOWN_STATUS : "No resolution for " + candidateCall.getCandidateDescriptor(); + failedCandidates.add(candidateCall); + } + } + OverloadResolutionResults results = computeResultAndReportErrors(trace, tracing, successfulCandidates, failedCandidates); if (!results.singleDescriptor()) { checkTypesWithNoCallee(trace, scope, task.getCall()); @@ -553,16 +555,15 @@ public class CallResolver { } } - private boolean checkAllValueArguments(JetScope scope, TracingStrategy tracing, ResolutionTask task, ResolvedCall candidateCall) { - boolean result = checkValueArgumentTypes(scope, candidateCall); - - result &= checkReceiver(tracing, candidateCall, candidateCall.getResultingDescriptor().getReceiverParameter(), candidateCall.getReceiverArgument(), task); - result &= checkReceiver(tracing, candidateCall, candidateCall.getResultingDescriptor().getExpectedThisObject(), candidateCall.getThisObject(), task); + private ResolutionStatus checkAllValueArguments(JetScope scope, TracingStrategy tracing, ResolutionTask task, ResolvedCall candidateCall) { + ResolutionStatus result = checkValueArgumentTypes(scope, candidateCall); + result = result.combine(checkReceiver(tracing, candidateCall, candidateCall.getResultingDescriptor().getReceiverParameter(), candidateCall.getReceiverArgument(), task)); + result = result.combine(checkReceiver(tracing, candidateCall, candidateCall.getResultingDescriptor().getExpectedThisObject(), candidateCall.getThisObject(), task)); return result; } - private boolean checkReceiver(TracingStrategy tracing, ResolvedCall candidateCall, ReceiverDescriptor receiverParameter, ReceiverDescriptor receiverArgument, ResolutionTask task) { - boolean result = true; + private ResolutionStatus checkReceiver(TracingStrategy tracing, ResolvedCall candidateCall, ReceiverDescriptor receiverParameter, ReceiverDescriptor receiverArgument, ResolutionTask task) { + ResolutionStatus result = SUCCESS; if (receiverParameter.exists() && receiverArgument.exists()) { ASTNode callOperationNode = task.getCall().getCallOperationNode(); boolean safeAccess = callOperationNode != null && callOperationNode.getElementType() == JetTokens.SAFE_ACCESS; @@ -570,7 +571,7 @@ public class CallResolver { AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(task.getDataFlowInfo(), candidateCall.getTrace().getBindingContext()); if (!safeAccess && !receiverParameter.getType().isNullable() && !autoCastService.isNotNull(receiverArgument)) { tracing.unsafeCall(candidateCall.getTrace(), receiverArgumentType); -// result = false; + result = UNSAFE_CALL_ERROR; } else { JetType effectiveReceiverArgumentType = safeAccess @@ -578,7 +579,7 @@ public class CallResolver { : receiverArgumentType; if (!semanticServices.getTypeChecker().isSubtypeOf(effectiveReceiverArgumentType, receiverParameter.getType())) { tracing.wrongReceiverType(candidateCall.getTrace(), receiverParameter, receiverArgument); - result = false; + result = OTHER_ERROR; } } @@ -589,8 +590,8 @@ public class CallResolver { return result; } - private boolean checkValueArgumentTypes(JetScope scope, ResolvedCall candidateCall) { - boolean result = true; + private ResolutionStatus checkValueArgumentTypes(JetScope scope, ResolvedCall candidateCall) { + ResolutionStatus result = SUCCESS; for (Map.Entry entry : candidateCall.getValueArguments().entrySet()) { ValueParameterDescriptor parameterDescriptor = entry.getKey(); ResolvedValueArgument resolvedArgument = entry.getValue(); @@ -625,97 +626,35 @@ public class CallResolver { // } // } // else { - result = false; + result = OTHER_ERROR; } } } return result; } -// private boolean checkReceiver(ResolutionTask task, ResolvedCall resolvedCall, TracingStrategy tracing, D candidate) { -// if (!checkReceiverAbsence(resolvedCall, tracing, candidate)) return false; -// ReceiverDescriptor actualReceiver = resolvedCall.getReceiverArgument(); -// ReceiverDescriptor expectedReceiver = candidate.getReceiverParameter(); -// -// if (actualReceiver.exists() -// && expectedReceiver.exists()) { -// if (!semanticServices.getTypeChecker().isSubtypeOf(actualReceiver.getType(), expectedReceiver.getType())) { -// tracing.missingReceiver(resolvedCall.getTrace(), expectedReceiver); -// return false; -// } -// } -// return true; -// } -// -// private boolean checkReceiverAbsence(ResolvedCall resolvedCall, TracingStrategy tracing, D candidate) { -// ReceiverDescriptor receiver = resolvedCall.getReceiverArgument(); -// ReceiverDescriptor candidateReceiver = candidate.getReceiverParameter(); -// if (receiver.exists()) { -// if (!candidateReceiver.exists()) { -// tracing.noReceiverAllowed(resolvedCall.getTrace()); -// return false; -// } -// } -// else if (candidateReceiver.exists()) { -// tracing.missingReceiver(resolvedCall.getTrace(), candidateReceiver); -// return false; -// } -// return true; -// } -// @NotNull private OverloadResolutionResults computeResultAndReportErrors( BindingTrace trace, TracingStrategy tracing, - Set> successfulCandidates, // original -> substituted + Set> successfulCandidates, Set> failedCandidates) { // TODO : maybe it's better to filter overrides out first, and only then look for the maximally specific if (successfulCandidates.size() > 0) { - if (successfulCandidates.size() != 1) { - Set> cleanCandidates = Sets.newLinkedHashSet(successfulCandidates); - boolean allClean = true; - for (Iterator> iterator = cleanCandidates.iterator(); iterator.hasNext(); ) { - ResolvedCall candidate = iterator.next(); - if (candidate.isDirty()) { - iterator.remove(); - allClean = false; - } - } - - if (cleanCandidates.isEmpty()) { - cleanCandidates = successfulCandidates; - } - ResolvedCall maximallySpecific = overloadingConflictResolver.findMaximallySpecific(cleanCandidates, false); - if (maximallySpecific != null) { - return OverloadResolutionResults.success(maximallySpecific); - } - - ResolvedCall maximallySpecificGenericsDiscriminated = overloadingConflictResolver.findMaximallySpecific(cleanCandidates, true); - if (maximallySpecificGenericsDiscriminated != null) { - return OverloadResolutionResults.success(maximallySpecificGenericsDiscriminated); - } - - Set> noOverrides = OverridingUtil.filterOverrides(successfulCandidates, MAP_TO_RESULT); - if (allClean) { -// tracing.reportOverallResolutionError(trace, "Overload resolution ambiguity: " -// + makeErrorMessageForMultipleDescriptors(noOverrides)); - tracing.ambiguity(trace, noOverrides); - } - - tracing.recordAmbiguity(trace, noOverrides); - - return OverloadResolutionResults.ambiguity(noOverrides); - } - else { - ResolvedCall result = successfulCandidates.iterator().next(); - - TemporaryBindingTrace temporaryTrace = result.getTrace(); - temporaryTrace.commit(); - return OverloadResolutionResults.success(result); - } + return chooseAndReportMaximallySpecific(trace, tracing, successfulCandidates); } else if (!failedCandidates.isEmpty()) { if (failedCandidates.size() != 1) { + Set> weakErrors = Sets.newLinkedHashSet(); + for (ResolvedCall candidate : failedCandidates) { + if (candidate.getStatus().isWeakError()) { + weakErrors.add(candidate); + } + } + if (!weakErrors.isEmpty()) { + return chooseAndReportMaximallySpecific(trace, tracing, weakErrors); + } + Set> noOverrides = OverridingUtil.filterOverrides(failedCandidates, MAP_TO_CANDIDATE); if (noOverrides.size() != 1) { // tracing.reportOverallResolutionError(trace, "None of the following functions can be called with the arguments supplied: " @@ -736,6 +675,51 @@ public class CallResolver { } } + private OverloadResolutionResults chooseAndReportMaximallySpecific(BindingTrace trace, TracingStrategy tracing, Set> candidates) { + if (candidates.size() != 1) { + Set> cleanCandidates = Sets.newLinkedHashSet(candidates); + boolean allClean = true; + for (Iterator> iterator = cleanCandidates.iterator(); iterator.hasNext(); ) { + ResolvedCall candidate = iterator.next(); + if (candidate.isDirty()) { + iterator.remove(); + allClean = false; + } + } + + if (cleanCandidates.isEmpty()) { + cleanCandidates = candidates; + } + ResolvedCall maximallySpecific = overloadingConflictResolver.findMaximallySpecific(cleanCandidates, false); + if (maximallySpecific != null) { + return OverloadResolutionResults.success(maximallySpecific); + } + + ResolvedCall maximallySpecificGenericsDiscriminated = overloadingConflictResolver.findMaximallySpecific(cleanCandidates, true); + if (maximallySpecificGenericsDiscriminated != null) { + return OverloadResolutionResults.success(maximallySpecificGenericsDiscriminated); + } + + Set> noOverrides = OverridingUtil.filterOverrides(candidates, MAP_TO_RESULT); + if (allClean) { +// tracing.reportOverallResolutionError(trace, "Overload resolution ambiguity: " +// + makeErrorMessageForMultipleDescriptors(noOverrides)); + tracing.ambiguity(trace, noOverrides); + } + + tracing.recordAmbiguity(trace, noOverrides); + + return OverloadResolutionResults.ambiguity(noOverrides); + } + else { + ResolvedCall result = candidates.iterator().next(); + + TemporaryBindingTrace temporaryTrace = result.getTrace(); + temporaryTrace.commit(); + return OverloadResolutionResults.success(result); + } + } + public void checkGenericBoundsInAFunctionCall(List jetTypeArguments, List typeArguments, CallableDescriptor functionDescriptor, BindingTrace trace) { Map context = Maps.newHashMap(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionStatus.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionStatus.java new file mode 100644 index 00000000000..7c9c5406285 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionStatus.java @@ -0,0 +1,35 @@ +package org.jetbrains.jet.lang.resolve.calls; + +/** + * @author abreslav + */ +public enum ResolutionStatus { + UNKNOWN_STATUS, + UNSAFE_CALL_ERROR, + OTHER_ERROR, + SUCCESS(true); + + private final boolean success; + + private ResolutionStatus(boolean success) { + this.success = success; + } + + private ResolutionStatus() { + this(false); + } + + public boolean isSuccess() { + return success; + } + + public ResolutionStatus combine(ResolutionStatus other) { + if (this.isSuccess()) return other; + if (this.isWeakError() && !other.isSuccess()) return other; + return this; + } + + public boolean isWeakError() { + return this == UNSAFE_CALL_ERROR; + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCall.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCall.java index a180750d356..73021e8890f 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCall.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCall.java @@ -15,6 +15,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import static org.jetbrains.jet.lang.resolve.calls.ResolutionStatus.UNKNOWN_STATUS; import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor.NO_RECEIVER; /** @@ -59,11 +60,21 @@ public class ResolvedCall { private final Map valueArguments = Maps.newHashMap(); private boolean someArgumentHasNoType = false; private TemporaryBindingTrace trace; + private ResolutionStatus status = UNKNOWN_STATUS; private ResolvedCall(@NotNull D candidateDescriptor) { this.candidateDescriptor = candidateDescriptor; } + @NotNull + public ResolutionStatus getStatus() { + return status; + } + + public void setStatus(@NotNull ResolutionStatus status) { + this.status = status; + } + @NotNull public TemporaryBindingTrace getTrace() { return trace; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index a60c73ab053..a23eace360d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -309,7 +309,7 @@ public class JetTypeInferrer { private void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, @NotNull final JetType expectedReturnType, @NotNull DataFlowInfo dataFlowInfo) { JetExpression bodyExpression = function.getBodyExpression(); - assert bodyExpression != null; + if (bodyExpression == null) return; final boolean blockBody = function.hasBlockBody(); final TypeInferenceContext context = diff --git a/idea/testData/checkerWithErrorTypes/full/ExtensionFunctions.jet b/idea/testData/checkerWithErrorTypes/full/ExtensionFunctions.jet index 54edfbaa527..72d955b6e3f 100644 --- a/idea/testData/checkerWithErrorTypes/full/ExtensionFunctions.jet +++ b/idea/testData/checkerWithErrorTypes/full/ExtensionFunctions.jet @@ -58,9 +58,9 @@ namespace null_safety { command.foo command.equals(null) - command?.equals(null) + command?.equals(null) command.equals1(null) - command?.equals1(null) + command?.equals1(null) val c = Command() c?.equals2(null)