001 /*
002 * Copyright 2010-2016 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.resolve.calls.results;
018
019 import com.google.common.collect.Sets;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
022 import org.jetbrains.kotlin.config.LanguageFeature;
023 import org.jetbrains.kotlin.config.LanguageVersionSettings;
024 import org.jetbrains.kotlin.descriptors.CallableDescriptor;
025 import org.jetbrains.kotlin.resolve.BindingTrace;
026 import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
027 import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext;
028 import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode;
029 import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall;
030 import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy;
031 import org.jetbrains.kotlin.resolve.calls.tower.TowerUtilsKt;
032
033 import java.util.*;
034
035 import static org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.*;
036
037 public class ResolutionResultsHandler {
038
039 private final OverloadingConflictResolver<MutableResolvedCall<?>> overloadingConflictResolver;
040
041 public ResolutionResultsHandler(
042 @NotNull KotlinBuiltIns builtIns,
043 @NotNull TypeSpecificityComparator specificityComparator
044 ) {
045 overloadingConflictResolver = FlatSignatureForResolvedCallKt.createOverloadingConflictResolver(builtIns, specificityComparator);
046 }
047
048 @NotNull
049 public <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> computeResultAndReportErrors(
050 @NotNull CallResolutionContext context,
051 @NotNull TracingStrategy tracing,
052 @NotNull Collection<MutableResolvedCall<D>> candidates,
053 @NotNull LanguageVersionSettings languageVersionSettings
054 ) {
055 Set<MutableResolvedCall<D>> successfulCandidates = Sets.newLinkedHashSet();
056 Set<MutableResolvedCall<D>> failedCandidates = Sets.newLinkedHashSet();
057 Set<MutableResolvedCall<D>> incompleteCandidates = Sets.newLinkedHashSet();
058 Set<MutableResolvedCall<D>> candidatesWithWrongReceiver = Sets.newLinkedHashSet();
059 for (MutableResolvedCall<D> candidateCall : candidates) {
060 ResolutionStatus status = candidateCall.getStatus();
061 assert status != UNKNOWN_STATUS : "No resolution for " + candidateCall.getCandidateDescriptor();
062 if (status.isSuccess()) {
063 successfulCandidates.add(candidateCall);
064 }
065 else if (status == INCOMPLETE_TYPE_INFERENCE) {
066 incompleteCandidates.add(candidateCall);
067 }
068 else if (candidateCall.getStatus() == RECEIVER_TYPE_ERROR) {
069 candidatesWithWrongReceiver.add(candidateCall);
070 }
071 else if (candidateCall.getStatus() != RECEIVER_PRESENCE_ERROR) {
072 failedCandidates.add(candidateCall);
073 }
074 }
075 // TODO : maybe it's better to filter overrides out first, and only then look for the maximally specific
076
077 if (!successfulCandidates.isEmpty() || !incompleteCandidates.isEmpty()) {
078 return computeSuccessfulResult(
079 context, tracing, successfulCandidates, incompleteCandidates, context.checkArguments, languageVersionSettings);
080 }
081 else if (!failedCandidates.isEmpty()) {
082 return computeFailedResult(tracing, context.trace, failedCandidates, context.checkArguments, languageVersionSettings);
083 }
084 if (!candidatesWithWrongReceiver.isEmpty()) {
085 tracing.unresolvedReferenceWrongReceiver(context.trace, candidatesWithWrongReceiver);
086 return OverloadResolutionResultsImpl.candidatesWithWrongReceiver(candidatesWithWrongReceiver);
087 }
088 tracing.unresolvedReference(context.trace);
089 return OverloadResolutionResultsImpl.nameNotFound();
090 }
091
092 @NotNull
093 private <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> computeSuccessfulResult(
094 @NotNull CallResolutionContext context,
095 @NotNull TracingStrategy tracing,
096 @NotNull Set<MutableResolvedCall<D>> successfulCandidates,
097 @NotNull Set<MutableResolvedCall<D>> incompleteCandidates,
098 @NotNull CheckArgumentTypesMode checkArgumentsMode,
099 @NotNull LanguageVersionSettings languageVersionSettings
100 ) {
101 Set<MutableResolvedCall<D>> successfulAndIncomplete = Sets.newLinkedHashSet();
102 successfulAndIncomplete.addAll(successfulCandidates);
103 successfulAndIncomplete.addAll(incompleteCandidates);
104 OverloadResolutionResultsImpl<D> results = chooseAndReportMaximallySpecific(
105 successfulAndIncomplete, true, context.isDebuggerContext, checkArgumentsMode, languageVersionSettings);
106 if (results.isSingleResult()) {
107 MutableResolvedCall<D> resultingCall = results.getResultingCall();
108 resultingCall.getTrace().moveAllMyDataTo(context.trace);
109 if (resultingCall.getStatus() == INCOMPLETE_TYPE_INFERENCE) {
110 return OverloadResolutionResultsImpl.incompleteTypeInference(resultingCall);
111 }
112 }
113 if (results.isAmbiguity()) {
114 tracing.recordAmbiguity(context.trace, results.getResultingCalls());
115 boolean allCandidatesIncomplete = allIncomplete(results.getResultingCalls());
116 // This check is needed for the following case:
117 // x.foo(unresolved) -- if there are multiple foo's, we'd report an ambiguity, and it does not make sense here
118 if (context.checkArguments != CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS ||
119 !CallUtilKt.hasUnresolvedArguments(context.call, context)) {
120 if (allCandidatesIncomplete) {
121 tracing.cannotCompleteResolve(context.trace, results.getResultingCalls());
122 }
123 else {
124 tracing.ambiguity(context.trace, results.getResultingCalls());
125 }
126 }
127 if (allCandidatesIncomplete) {
128 return OverloadResolutionResultsImpl.incompleteTypeInference(results.getResultingCalls());
129 }
130 }
131 return results;
132 }
133
134 @NotNull
135 private <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> computeFailedResult(
136 @NotNull TracingStrategy tracing,
137 @NotNull BindingTrace trace,
138 @NotNull Set<MutableResolvedCall<D>> failedCandidates,
139 @NotNull CheckArgumentTypesMode checkArgumentsMode,
140 @NotNull LanguageVersionSettings languageVersionSettings
141 ) {
142 if (failedCandidates.size() == 1) {
143 return recordFailedInfo(tracing, trace, failedCandidates);
144 }
145
146 for (EnumSet<ResolutionStatus> severityLevel : SEVERITY_LEVELS) {
147 Set<MutableResolvedCall<D>> thisLevel = Sets.newLinkedHashSet();
148 for (MutableResolvedCall<D> candidate : failedCandidates) {
149 if (severityLevel.contains(candidate.getStatus())) {
150 thisLevel.add(candidate);
151 }
152 }
153 if (!thisLevel.isEmpty()) {
154 if (severityLevel.contains(ARGUMENTS_MAPPING_ERROR)) {
155 @SuppressWarnings("unchecked")
156 OverloadingConflictResolver<MutableResolvedCall<D>> myResolver = (OverloadingConflictResolver) overloadingConflictResolver;
157 return recordFailedInfo(tracing, trace, myResolver.filterOutEquivalentCalls(new LinkedHashSet<MutableResolvedCall<D>>(thisLevel)));
158 }
159 OverloadResolutionResultsImpl<D> results = chooseAndReportMaximallySpecific(
160 thisLevel, false, false, checkArgumentsMode, languageVersionSettings);
161 return recordFailedInfo(tracing, trace, results.getResultingCalls());
162 }
163 }
164
165 throw new AssertionError("Should not be reachable, cause every status must belong to some level: " + failedCandidates);
166 }
167
168 @NotNull
169 private static <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> recordFailedInfo(
170 @NotNull TracingStrategy tracing,
171 @NotNull BindingTrace trace,
172 @NotNull Collection<MutableResolvedCall<D>> candidates
173 ) {
174 if (candidates.size() == 1) {
175 MutableResolvedCall<D> failed = candidates.iterator().next();
176 failed.getTrace().moveAllMyDataTo(trace);
177 return OverloadResolutionResultsImpl.singleFailedCandidate(failed);
178 }
179 tracing.noneApplicable(trace, candidates);
180 tracing.recordAmbiguity(trace, candidates);
181 return OverloadResolutionResultsImpl.manyFailedCandidates(candidates);
182 }
183
184 private static <D extends CallableDescriptor> boolean allIncomplete(@NotNull Collection<MutableResolvedCall<D>> results) {
185 for (MutableResolvedCall<D> result : results) {
186 if (result.getStatus() != INCOMPLETE_TYPE_INFERENCE) return false;
187 }
188 return true;
189 }
190
191 @NotNull
192 private <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> chooseAndReportMaximallySpecific(
193 @NotNull Set<MutableResolvedCall<D>> candidates,
194 boolean discriminateGenerics,
195 boolean isDebuggerContext,
196 @NotNull CheckArgumentTypesMode checkArgumentsMode,
197 @NotNull LanguageVersionSettings languageVersionSettings
198 ) {
199 OverloadingConflictResolver<MutableResolvedCall<D>> myResolver = (OverloadingConflictResolver) overloadingConflictResolver;
200
201 Set<MutableResolvedCall<D>> refinedCandidates = candidates;
202 if (!languageVersionSettings.supportsFeature(LanguageFeature.RefinedSamAdaptersPriority)) {
203 Set<MutableResolvedCall<D>> nonSynthesized = new HashSet<MutableResolvedCall<D>>();
204 for (MutableResolvedCall<D> candidate : candidates) {
205 if (!TowerUtilsKt.isSynthesized(candidate.getCandidateDescriptor())) {
206 nonSynthesized.add(candidate);
207 }
208 }
209
210 if (!nonSynthesized.isEmpty()) {
211 refinedCandidates = nonSynthesized;
212 }
213 }
214
215 Set<MutableResolvedCall<D>> specificCalls =
216 myResolver.chooseMaximallySpecificCandidates(refinedCandidates, checkArgumentsMode, discriminateGenerics, isDebuggerContext);
217
218 if (specificCalls.size() == 1) {
219 return OverloadResolutionResultsImpl.success(specificCalls.iterator().next());
220 }
221 else {
222 return OverloadResolutionResultsImpl.ambiguity(specificCalls);
223 }
224 }
225 }