Weak errors introduced

This commit is contained in:
Andrey Breslav
2011-10-13 14:39:47 +04:00
parent ae4ca1ef21
commit a1be8afd61
5 changed files with 139 additions and 109 deletions
@@ -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 <D extends CallableDescriptor> OverloadResolutionResults<D> performResolution(@NotNull BindingTrace trace, @NotNull JetScope scope, @NotNull JetType expectedType, @NotNull ResolutionTask<D> task, @NotNull TracingStrategy tracing) {
Set<ResolvedCall<D>> successfulCandidates = Sets.newLinkedHashSet();
Set<ResolvedCall<D>> failedCandidates = Sets.newLinkedHashSet();
for (ResolvedCall<D> 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<ResolvedCall<D>> successfulCandidates = Sets.newLinkedHashSet();
Set<ResolvedCall<D>> failedCandidates = Sets.newLinkedHashSet();
for (ResolvedCall<D> 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<D> results = computeResultAndReportErrors(trace, tracing, successfulCandidates, failedCandidates);
if (!results.singleDescriptor()) {
checkTypesWithNoCallee(trace, scope, task.getCall());
@@ -553,16 +555,15 @@ public class CallResolver {
}
}
private <D extends CallableDescriptor> boolean checkAllValueArguments(JetScope scope, TracingStrategy tracing, ResolutionTask<D> task, ResolvedCall<D> 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 <D extends CallableDescriptor> ResolutionStatus checkAllValueArguments(JetScope scope, TracingStrategy tracing, ResolutionTask<D> task, ResolvedCall<D> 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 <D extends CallableDescriptor> boolean checkReceiver(TracingStrategy tracing, ResolvedCall<D> candidateCall, ReceiverDescriptor receiverParameter, ReceiverDescriptor receiverArgument, ResolutionTask<D> task) {
boolean result = true;
private <D extends CallableDescriptor> ResolutionStatus checkReceiver(TracingStrategy tracing, ResolvedCall<D> candidateCall, ReceiverDescriptor receiverParameter, ReceiverDescriptor receiverArgument, ResolutionTask<D> 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 <D extends CallableDescriptor> boolean checkValueArgumentTypes(JetScope scope, ResolvedCall<D> candidateCall) {
boolean result = true;
private <D extends CallableDescriptor> ResolutionStatus checkValueArgumentTypes(JetScope scope, ResolvedCall<D> candidateCall) {
ResolutionStatus result = SUCCESS;
for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> 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 <D extends CallableDescriptor> boolean checkReceiver(ResolutionTask<D> task, ResolvedCall<D> 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 <D extends CallableDescriptor> boolean checkReceiverAbsence(ResolvedCall<D> 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 <D extends CallableDescriptor> OverloadResolutionResults<D> computeResultAndReportErrors(
BindingTrace trace,
TracingStrategy tracing,
Set<ResolvedCall<D>> successfulCandidates, // original -> substituted
Set<ResolvedCall<D>> successfulCandidates,
Set<ResolvedCall<D>> 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<ResolvedCall<D>> cleanCandidates = Sets.newLinkedHashSet(successfulCandidates);
boolean allClean = true;
for (Iterator<ResolvedCall<D>> iterator = cleanCandidates.iterator(); iterator.hasNext(); ) {
ResolvedCall<D> candidate = iterator.next();
if (candidate.isDirty()) {
iterator.remove();
allClean = false;
}
}
if (cleanCandidates.isEmpty()) {
cleanCandidates = successfulCandidates;
}
ResolvedCall<D> maximallySpecific = overloadingConflictResolver.findMaximallySpecific(cleanCandidates, false);
if (maximallySpecific != null) {
return OverloadResolutionResults.success(maximallySpecific);
}
ResolvedCall<D> maximallySpecificGenericsDiscriminated = overloadingConflictResolver.findMaximallySpecific(cleanCandidates, true);
if (maximallySpecificGenericsDiscriminated != null) {
return OverloadResolutionResults.success(maximallySpecificGenericsDiscriminated);
}
Set<ResolvedCall<D>> 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<D> 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<ResolvedCall<D>> weakErrors = Sets.newLinkedHashSet();
for (ResolvedCall<D> candidate : failedCandidates) {
if (candidate.getStatus().isWeakError()) {
weakErrors.add(candidate);
}
}
if (!weakErrors.isEmpty()) {
return chooseAndReportMaximallySpecific(trace, tracing, weakErrors);
}
Set<ResolvedCall<D>> 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 <D extends CallableDescriptor> OverloadResolutionResults<D> chooseAndReportMaximallySpecific(BindingTrace trace, TracingStrategy tracing, Set<ResolvedCall<D>> candidates) {
if (candidates.size() != 1) {
Set<ResolvedCall<D>> cleanCandidates = Sets.newLinkedHashSet(candidates);
boolean allClean = true;
for (Iterator<ResolvedCall<D>> iterator = cleanCandidates.iterator(); iterator.hasNext(); ) {
ResolvedCall<D> candidate = iterator.next();
if (candidate.isDirty()) {
iterator.remove();
allClean = false;
}
}
if (cleanCandidates.isEmpty()) {
cleanCandidates = candidates;
}
ResolvedCall<D> maximallySpecific = overloadingConflictResolver.findMaximallySpecific(cleanCandidates, false);
if (maximallySpecific != null) {
return OverloadResolutionResults.success(maximallySpecific);
}
ResolvedCall<D> maximallySpecificGenericsDiscriminated = overloadingConflictResolver.findMaximallySpecific(cleanCandidates, true);
if (maximallySpecificGenericsDiscriminated != null) {
return OverloadResolutionResults.success(maximallySpecificGenericsDiscriminated);
}
Set<ResolvedCall<D>> 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<D> result = candidates.iterator().next();
TemporaryBindingTrace temporaryTrace = result.getTrace();
temporaryTrace.commit();
return OverloadResolutionResults.success(result);
}
}
public void checkGenericBoundsInAFunctionCall(List<JetTypeProjection> jetTypeArguments, List<JetType> typeArguments, CallableDescriptor functionDescriptor, BindingTrace trace) {
Map<TypeConstructor, TypeProjection> context = Maps.newHashMap();
@@ -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;
}
}
@@ -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<D extends CallableDescriptor> {
private final Map<ValueParameterDescriptor, ResolvedValueArgument> 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;
@@ -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 =
@@ -58,9 +58,9 @@ namespace null_safety {
command<!UNSAFE_CALL!>.<!>foo
command.equals(null)
command<!UNNECESSARY_SAFE_CALL!>?.<!>equals(null)
command?.equals(null)
command.equals1(null)
command<!UNNECESSARY_SAFE_CALL!>?.<!>equals1(null)
command?.equals1(null)
val c = Command()
c<!UNNECESSARY_SAFE_CALL!>?.<!>equals2(null)