diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DelegatingBindingTrace.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DelegatingBindingTrace.java index 9b00c34e64a..416a0b54e5d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DelegatingBindingTrace.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DelegatingBindingTrace.java @@ -70,6 +70,11 @@ public class DelegatingBindingTrace implements BindingTrace { } } + public void clear() { + map.clear(); + diagnostics.clear(); + } + @Override public void report(@NotNull Diagnostic diagnostic) { diagnostics.add(diagnostic); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TemporaryBindingTrace.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TemporaryBindingTrace.java index 9d7f3fd1a18..fef2b6a9e1a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TemporaryBindingTrace.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TemporaryBindingTrace.java @@ -11,6 +11,7 @@ public class TemporaryBindingTrace extends DelegatingBindingTrace { private final BindingTrace trace; + private TemporaryBindingTrace(BindingTrace trace) { super(trace.getBindingContext()); this.trace = trace; @@ -18,5 +19,6 @@ public class TemporaryBindingTrace extends DelegatingBindingTrace { public void commit() { addAllMyDataTo(trace); + clear(); } } 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 3840d59e444..976d5b9c070 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 @@ -206,8 +206,6 @@ public class CallResolver { @NotNull JetType expectedType, @NotNull final List> prioritizedTasks, // high to low priority @NotNull final JetReferenceExpression reference) { - TemporaryBindingTrace traceForFirstNonemptyCandidateSet = null; - OverloadResolutionResults resultsForFirstNonemptyCandidateSet = null; TracingStrategy tracing = new TracingStrategy() { @Override public void bindReference(@NotNull BindingTrace trace, @NotNull ResolvedCallImpl resolvedCall) { @@ -272,12 +270,12 @@ public class CallResolver { } @Override - public void ambiguity(@NotNull BindingTrace trace, @NotNull Set> descriptors) { + public void ambiguity(@NotNull BindingTrace trace, @NotNull Collection> descriptors) { trace.report(OVERLOAD_RESOLUTION_AMBIGUITY.on(call.getCallNode(), descriptors)); } @Override - public void noneApplicable(@NotNull BindingTrace trace, @NotNull Set> descriptors) { + public void noneApplicable(@NotNull BindingTrace trace, @NotNull Collection> descriptors) { trace.report(NONE_APPLICABLE.on(reference, descriptors)); } @@ -323,6 +321,9 @@ public class CallResolver { trace.report(UNNECESSARY_SAFE_CALL.on((JetElement) callOperationNode.getTreeParent().getPsi(), callOperationNode, type)); } }; + + TemporaryBindingTrace traceForFirstNonemptyCandidateSet = null; + OverloadResolutionResults resultsForFirstNonemptyCandidateSet = null; for (ResolutionTask task : prioritizedTasks) { TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(trace); OverloadResolutionResults results = performResolution(temporaryTrace, scope, expectedType, task, tracing); @@ -479,6 +480,14 @@ public class CallResolver { task.performAdvancedChecks(candidate, temporaryTrace, tracing); + // 'super' cannot be passed as an argument, for receiver arguments expression typer does not track this + // See TaskPrioritizer for more + JetSuperExpression superExpression = TaskPrioritizer.getReceiverSuper(candidateCall.getReceiverArgument()); + if (superExpression != null) { + temporaryTrace.report(SUPER_IS_NOT_AN_EXPRESSION.on(superExpression, superExpression.getText())); + candidateCall.setStatus(OTHER_ERROR); + } + recordAutoCastIfNecessary(candidateCall.getReceiverArgument(), candidateCall.getTrace()); recordAutoCastIfNecessary(candidateCall.getThisObject(), candidateCall.getTrace()); } @@ -540,21 +549,6 @@ public class CallResolver { } } -// private Function createMapFunction(D substitutedFunctionDescriptor) { -// assert substitutedFunctionDescriptor != null; -// final Map parameterMap = Maps.newHashMap(); -// for (ValueParameterDescriptor valueParameterDescriptor : substitutedFunctionDescriptor.getValueParameters()) { -// parameterMap.put(valueParameterDescriptor.getOriginal(), valueParameterDescriptor); -// } -// -// return new Function() { -// @Override -// public ValueParameterDescriptor apply(ValueParameterDescriptor input) { -// return parameterMap.get(input.getOriginal()); -// } -// }; -// } - private void replaceValueParametersWithSubstitutedOnes(ResolvedCallImpl candidateCall, @NotNull D substitutedDescriptor) { Map parameterMap = Maps.newHashMap(); for (ValueParameterDescriptor valueParameterDescriptor : substitutedDescriptor.getValueParameters()) { @@ -656,30 +650,47 @@ public class CallResolver { 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) { - return chooseAndReportMaximallySpecific(trace, tracing, successfulCandidates); + OverloadResolutionResults results = chooseAndReportMaximallySpecific(successfulCandidates); + if (results.isAmbiguity()) { + // This check is needed for the following case: + // x.foo(unresolved) -- if there are multiple foo's, we'd report an ambiguity, and it does not make sense here + if (allClean(results.getResults())) { + tracing.ambiguity(trace, results.getResults()); + } + tracing.recordAmbiguity(trace, results.getResults()); + } + return results; } else if (!failedCandidates.isEmpty()) { if (failedCandidates.size() != 1) { - Set> weakErrors = Sets.newLinkedHashSet(); - for (ResolvedCallImpl candidate : failedCandidates) { - if (candidate.getStatus().isWeakError()) { - weakErrors.add(candidate); - } - } - if (!weakErrors.isEmpty()) { - OverloadResolutionResults results = chooseAndReportMaximallySpecific(trace, tracing, weakErrors); - if (results.isSuccess()) { - return OverloadResolutionResults.singleFailedCandidate(results.getResult()); + // This is needed when there are several overloads some of which are OK but for nullability of the receiver, + // and some are not OK at all. In this case we'd like to say "unsafe call" rather than "none applicable" + // Used to be: weak errors. Generalized for future extensions + for (EnumSet severityLevel : SEVERITY_LEVELS) { + Set> thisLevel = Sets.newLinkedHashSet(); + for (ResolvedCallImpl candidate : failedCandidates) { + if (severityLevel.contains(candidate.getStatus())) { + thisLevel.add(candidate); + } } + if (!thisLevel.isEmpty()) { + OverloadResolutionResults results = chooseAndReportMaximallySpecific(thisLevel); + if (results.isSuccess()) { + results.getResult().getTrace().commit(); + return OverloadResolutionResults.singleFailedCandidate(results.getResult()); + } - return OverloadResolutionResults.manyFailedCandidates(results.getResults()); + tracing.noneApplicable(trace, results.getResults()); + tracing.recordAmbiguity(trace, results.getResults()); + return OverloadResolutionResults.manyFailedCandidates(results.getResults()); + } } + assert false : "Should not be reachable, cause every status must belong to some level"; 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: " -// + makeErrorMessageForMultipleDescriptors(noOverrides)); tracing.noneApplicable(trace, noOverrides); tracing.recordAmbiguity(trace, noOverrides); return OverloadResolutionResults.manyFailedCandidates(noOverrides); @@ -696,15 +707,20 @@ public class CallResolver { } } - private OverloadResolutionResults chooseAndReportMaximallySpecific(BindingTrace trace, TracingStrategy tracing, Set> candidates) { + private boolean allClean(Collection> results) { + for (ResolvedCallImpl result : results) { + if (result.isDirty()) return false; + } + return true; + } + + private OverloadResolutionResults chooseAndReportMaximallySpecific(Set> candidates) { if (candidates.size() != 1) { Set> cleanCandidates = Sets.newLinkedHashSet(candidates); - boolean allClean = true; for (Iterator> iterator = cleanCandidates.iterator(); iterator.hasNext(); ) { ResolvedCallImpl candidate = iterator.next(); if (candidate.isDirty()) { iterator.remove(); - allClean = false; } } @@ -722,13 +738,6 @@ public class CallResolver { } 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); } 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 index 7c9c5406285..78253d24466 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionStatus.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionStatus.java @@ -1,5 +1,7 @@ package org.jetbrains.jet.lang.resolve.calls; +import java.util.EnumSet; + /** * @author abreslav */ @@ -9,7 +11,14 @@ public enum ResolutionStatus { OTHER_ERROR, SUCCESS(true); + @SuppressWarnings("unchecked") + public static final EnumSet[] SEVERITY_LEVELS = new EnumSet[] { + EnumSet.of(UNSAFE_CALL_ERROR), // weakest + EnumSet.of(OTHER_ERROR), // most severe + }; + private final boolean success; + private int severityIndex = -1; private ResolutionStatus(boolean success) { this.success = success; @@ -25,11 +34,21 @@ public enum ResolutionStatus { public ResolutionStatus combine(ResolutionStatus other) { if (this.isSuccess()) return other; - if (this.isWeakError() && !other.isSuccess()) return other; + if (!other.isSuccess() && this.getSeverityIndex() < other.getSeverityIndex()) return other; return this; } + + private int getSeverityIndex() { + if (severityIndex == -1) { + for (int i = 0; i < SEVERITY_LEVELS.length; i++) { + if (SEVERITY_LEVELS[i].contains(this)) { + severityIndex = i; + break; + } + } + } + assert severityIndex >= 0; - public boolean isWeakError() { - return this == UNSAFE_CALL_ERROR; + return severityIndex; } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java index 5bfc0947a8e..3b02eb27f0e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java @@ -2,16 +2,20 @@ package org.jetbrains.jet.lang.resolve.calls; import com.google.common.collect.Lists; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; import org.jetbrains.jet.lang.psi.Call; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetSuperExpression; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastService; import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetTypeChecker; @@ -72,6 +76,18 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor return false; } + @Nullable + /*package*/ static JetSuperExpression getReceiverSuper(@NotNull ReceiverDescriptor receiver) { + if (receiver instanceof ExpressionReceiver) { + ExpressionReceiver expressionReceiver = (ExpressionReceiver) receiver; + JetExpression expression = expressionReceiver.getExpression(); + if (expression instanceof JetSuperExpression) { + return (JetSuperExpression) expression; + } + } + return null; + } + public List> computePrioritizedTasks(@NotNull JetScope scope, @NotNull Call call, @NotNull String name, @NotNull BindingContext bindingContext, @NotNull DataFlowInfo dataFlowInfo) { List> result = Lists.newArrayList(); @@ -108,8 +124,17 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor convertWithReceivers(membersForThisVariant, Collections.singletonList(variant), Collections.singletonList(NO_RECEIVER), members); } - addTask(result, call, locals, dataFlowInfo); - addTask(result, call, members, dataFlowInfo); + if (getReceiverSuper(receiver) != null) { + // If the call is of the form super.foo(), it can actually be only a member + // But if there's no appropriate member, we would like to report that super cannot be a receiver for an extension + // Thus, put members first + addTask(result, call, members, dataFlowInfo); + addTask(result, call, locals, dataFlowInfo); + } + else { + addTask(result, call, locals, dataFlowInfo); + addTask(result, call, members, dataFlowInfo); + } for (ReceiverDescriptor implicitReceiver : implicitReceivers) { Collection memberExtensions = getExtensionsByName(implicitReceiver.getType().getMemberScope(), name); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TracingStrategy.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TracingStrategy.java index 6284ccb5b0d..d06b99d2068 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TracingStrategy.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TracingStrategy.java @@ -8,7 +8,6 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.types.JetType; import java.util.Collection; -import java.util.Set; /** * @author abreslav @@ -40,10 +39,10 @@ import java.util.Set; public void wrongNumberOfTypeArguments(@NotNull BindingTrace trace, int expectedTypeArgumentCount) {} @Override - public void ambiguity(@NotNull BindingTrace trace, @NotNull Set> descriptors) {} + public void ambiguity(@NotNull BindingTrace trace, @NotNull Collection> descriptors) {} @Override - public void noneApplicable(@NotNull BindingTrace trace, @NotNull Set> descriptors) {} + public void noneApplicable(@NotNull BindingTrace trace, @NotNull Collection> descriptors) {} @Override public void instantiationOfAbstractClass(@NotNull BindingTrace trace) {} @@ -74,9 +73,9 @@ import java.util.Set; void wrongNumberOfTypeArguments(@NotNull BindingTrace trace, int expectedTypeArgumentCount); - void ambiguity(@NotNull BindingTrace trace, @NotNull Set> descriptors); + void ambiguity(@NotNull BindingTrace trace, @NotNull Collection> descriptors); - void noneApplicable(@NotNull BindingTrace trace, @NotNull Set> descriptors); + void noneApplicable(@NotNull BindingTrace trace, @NotNull Collection> descriptors); void instantiationOfAbstractClass(@NotNull BindingTrace trace); diff --git a/compiler/frontend/src/org/jetbrains/jet/util/slicedmap/MutableSlicedMap.java b/compiler/frontend/src/org/jetbrains/jet/util/slicedmap/MutableSlicedMap.java index a3706523fbf..32dfe3d17bf 100644 --- a/compiler/frontend/src/org/jetbrains/jet/util/slicedmap/MutableSlicedMap.java +++ b/compiler/frontend/src/org/jetbrains/jet/util/slicedmap/MutableSlicedMap.java @@ -8,4 +8,6 @@ public interface MutableSlicedMap extends SlicedMap { void put(WritableSlice slice, K key, V value); V remove(RemovableSlice slice, K key); + + void clear(); } diff --git a/compiler/frontend/src/org/jetbrains/jet/util/slicedmap/SlicedMapImpl.java b/compiler/frontend/src/org/jetbrains/jet/util/slicedmap/SlicedMapImpl.java index 36dbf297929..70182bb6ee4 100644 --- a/compiler/frontend/src/org/jetbrains/jet/util/slicedmap/SlicedMapImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/util/slicedmap/SlicedMapImpl.java @@ -47,6 +47,11 @@ public class SlicedMapImpl implements MutableSlicedMap { slice.afterPut(this, key, value); } + @Override + public void clear() { + map.clear(); + } + @Override public V get(ReadOnlySlice slice, K key) { SlicedMapKey slicedMapKey = slice.makeKey(key); diff --git a/compiler/testData/checkerWithErrorTypes/quick/ExtensionsCalledOnSuper.jet b/compiler/testData/checkerWithErrorTypes/quick/ExtensionsCalledOnSuper.jet new file mode 100644 index 00000000000..fe072bcc632 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/quick/ExtensionsCalledOnSuper.jet @@ -0,0 +1,21 @@ +trait T { + fun foo() {} + fun buzz() {} + fun buzz1(i : Int) {} +} + +fun T.bar() {} + +fun T.buzz() {} +fun T.buzz1() {} + +class C : T { + fun test() { + fun T.buzz() {} + fun T.buzz1() {} + super.foo() // OK + super.bar() // Error + super.buzz() // OK, resolved to a member + super.buzz1() // Resolved to a member, but error: no parameter passed where required + } +} \ No newline at end of file diff --git a/compiler/testData/checkerWithErrorTypes/quick/Super.jet b/compiler/testData/checkerWithErrorTypes/quick/Super.jet index e6d1146fd3c..5841b9108f3 100644 --- a/compiler/testData/checkerWithErrorTypes/quick/Super.jet +++ b/compiler/testData/checkerWithErrorTypes/quick/Super.jet @@ -1,5 +1,17 @@ namespace example; +fun any(a : Any) {} + +fun notAnExpression() { + any(super) // not an expression + if (super) {} else {} // not an expression + val x = super // not an expression + when (1) { + super => 1 // not an expression + } + +} + trait T { fun foo() {} }