001    /*
002     * Copyright 2010-2015 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.model;
018    
019    import com.google.common.collect.Maps;
020    import com.intellij.openapi.diagnostic.Logger;
021    import com.intellij.util.Function;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.kotlin.descriptors.CallableDescriptor;
025    import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
026    import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
027    import org.jetbrains.kotlin.psi.Call;
028    import org.jetbrains.kotlin.psi.ValueArgument;
029    import org.jetbrains.kotlin.resolve.DelegatingBindingTrace;
030    import org.jetbrains.kotlin.resolve.calls.callResolverUtil.CallResolverUtilKt;
031    import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
032    import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem;
033    import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus;
034    import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind;
035    import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionCandidate;
036    import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy;
037    import org.jetbrains.kotlin.resolve.scopes.receivers.Receiver;
038    import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
039    import org.jetbrains.kotlin.types.KotlinType;
040    import org.jetbrains.kotlin.types.TypeProjection;
041    import org.jetbrains.kotlin.types.TypeSubstitutor;
042    
043    import java.util.ArrayList;
044    import java.util.List;
045    import java.util.Map;
046    
047    import static org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.INCOMPLETE_TYPE_INFERENCE;
048    import static org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.UNKNOWN_STATUS;
049    
050    public class ResolvedCallImpl<D extends CallableDescriptor> implements MutableResolvedCall<D> {
051        private static final Logger LOG = Logger.getInstance(ResolvedCallImpl.class);
052    
053        public static final Function<MutableResolvedCall<?>, CallableDescriptor> MAP_TO_CANDIDATE = new Function<MutableResolvedCall<?>, CallableDescriptor>() {
054            @Override
055            public CallableDescriptor fun(MutableResolvedCall<?> resolvedCall) {
056                return resolvedCall.getCandidateDescriptor();
057            }
058        };
059    
060        public static final Function<MutableResolvedCall<?>, CallableDescriptor> MAP_TO_RESULT = new Function<MutableResolvedCall<?>, CallableDescriptor>() {
061            @Override
062            public CallableDescriptor fun(MutableResolvedCall<?> resolvedCall) {
063                return resolvedCall.getResultingDescriptor();
064            }
065        };
066    
067        @NotNull
068        public static <D extends CallableDescriptor> ResolvedCallImpl<D> create(
069                @NotNull ResolutionCandidate<D> candidate,
070                @NotNull DelegatingBindingTrace trace,
071                @NotNull TracingStrategy tracing,
072                @NotNull MutableDataFlowInfoForArguments dataFlowInfoForArguments
073        ) {
074            return new ResolvedCallImpl<D>(candidate, trace, tracing, dataFlowInfoForArguments);
075        }
076    
077        private final Call call;
078        private final D candidateDescriptor;
079        private D resultingDescriptor; // Probably substituted
080        private final ReceiverValue dispatchReceiver; // receiver object of a method
081        private final Receiver extensionReceiver; // receiver of an extension function
082        private final ExplicitReceiverKind explicitReceiverKind;
083        private final TypeSubstitutor knownTypeParametersSubstitutor;
084    
085        private final Map<TypeParameterDescriptor, KotlinType> typeArguments = Maps.newLinkedHashMap();
086        private final Map<ValueParameterDescriptor, ResolvedValueArgument> valueArguments = Maps.newLinkedHashMap();
087        private Map<ValueParameterDescriptor, ResolvedValueArgument> valueArgumentsBeforeSubstitution;
088        private final MutableDataFlowInfoForArguments dataFlowInfoForArguments;
089        private final Map<ValueArgument, ArgumentMatchImpl> argumentToParameterMap = Maps.newHashMap();
090    
091        private DelegatingBindingTrace trace;
092        private TracingStrategy tracing;
093        private ResolutionStatus status = UNKNOWN_STATUS;
094        private ConstraintSystem constraintSystem = null;
095        private Boolean hasInferredReturnType = null;
096        private boolean completed = false;
097    
098        private ResolvedCallImpl(
099                @NotNull ResolutionCandidate<D> candidate,
100                @NotNull DelegatingBindingTrace trace,
101                @NotNull TracingStrategy tracing,
102                @NotNull MutableDataFlowInfoForArguments dataFlowInfoForArguments
103        ) {
104            this.call = candidate.getCall();
105            this.candidateDescriptor = candidate.getDescriptor();
106            this.dispatchReceiver = candidate.getDispatchReceiver();
107            this.extensionReceiver = candidate.getReceiverArgument();
108            this.explicitReceiverKind = candidate.getExplicitReceiverKind();
109            this.knownTypeParametersSubstitutor = candidate.getKnownTypeParametersResultingSubstitutor();
110            this.trace = trace;
111            this.tracing = tracing;
112            this.dataFlowInfoForArguments = dataFlowInfoForArguments;
113        }
114    
115        public ResolvedCallImpl(
116                @NotNull Call call,
117                @NotNull D candidateDescriptor,
118                @Nullable ReceiverValue dispatchReceiver,
119                @Nullable ReceiverValue extensionReceiver,
120                @NotNull ExplicitReceiverKind explicitReceiverKind,
121                @Nullable TypeSubstitutor knownTypeParametersSubstitutor,
122                @NotNull DelegatingBindingTrace trace,
123                @NotNull TracingStrategy tracing,
124                @NotNull MutableDataFlowInfoForArguments dataFlowInfoForArguments
125        ) {
126            this.call = call;
127            this.candidateDescriptor = candidateDescriptor;
128            this.dispatchReceiver = dispatchReceiver;
129            this.extensionReceiver = extensionReceiver;
130            this.explicitReceiverKind = explicitReceiverKind;
131            this.knownTypeParametersSubstitutor = knownTypeParametersSubstitutor;
132            this.trace = trace;
133            this.tracing = tracing;
134            this.dataFlowInfoForArguments = dataFlowInfoForArguments;
135        }
136    
137        @Override
138        @NotNull
139        public ResolutionStatus getStatus() {
140            return status;
141        }
142    
143        @Override
144        public void addStatus(@NotNull ResolutionStatus status) {
145            this.status = this.status.combine(status);
146        }
147    
148        @Override
149        public void setStatusToSuccess() {
150            assert status == INCOMPLETE_TYPE_INFERENCE || status == UNKNOWN_STATUS;
151            status = ResolutionStatus.SUCCESS;
152        }
153    
154        @Override
155        @NotNull
156        public DelegatingBindingTrace getTrace() {
157            assertNotCompleted("Trace");
158            return trace;
159        }
160    
161        @Override
162        @NotNull
163        public TracingStrategy getTracingStrategy() {
164            assertNotCompleted("TracingStrategy");
165            return tracing;
166        }
167    
168        @NotNull
169        @Override
170        public Call getCall() {
171            return call;
172        }
173    
174        @Override
175        @NotNull
176        public D getCandidateDescriptor() {
177            return candidateDescriptor;
178        }
179    
180        @Override
181        @NotNull
182        public D getResultingDescriptor() {
183            return resultingDescriptor == null ? candidateDescriptor : resultingDescriptor;
184        }
185    
186        @Override
187        public void setResultingSubstitutor(@NotNull TypeSubstitutor substitutor) {
188            resultingDescriptor = (D) candidateDescriptor.substitute(substitutor);
189            assert resultingDescriptor != null : candidateDescriptor;
190    
191            for (TypeParameterDescriptor typeParameter : candidateDescriptor.getTypeParameters()) {
192                TypeProjection typeArgumentProjection = substitutor.getSubstitution().get(typeParameter.getDefaultType());
193                if (typeArgumentProjection != null) {
194                    typeArguments.put(typeParameter, typeArgumentProjection.getType());
195                }
196            }
197    
198            Map<ValueParameterDescriptor, ValueParameterDescriptor> substitutedParametersMap = Maps.newHashMap();
199            for (ValueParameterDescriptor valueParameterDescriptor : resultingDescriptor.getValueParameters()) {
200                substitutedParametersMap.put(valueParameterDescriptor.getOriginal(), valueParameterDescriptor);
201            }
202    
203            valueArgumentsBeforeSubstitution = Maps.newLinkedHashMap(valueArguments);
204            valueArguments.clear();
205            for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : valueArgumentsBeforeSubstitution.entrySet()) {
206                ValueParameterDescriptor substitutedVersion = substitutedParametersMap.get(entry.getKey().getOriginal());
207                assert substitutedVersion != null : entry.getKey();
208                valueArguments.put(substitutedVersion, entry.getValue());
209            }
210    
211            Map<ValueArgument, ArgumentMatchImpl> originalArgumentToParameterMap = Maps.newLinkedHashMap(argumentToParameterMap);
212            argumentToParameterMap.clear();
213            for (Map.Entry<ValueArgument, ArgumentMatchImpl> entry : originalArgumentToParameterMap.entrySet()) {
214                ArgumentMatchImpl argumentMatch = entry.getValue();
215                ValueParameterDescriptor valueParameterDescriptor = argumentMatch.getValueParameter();
216                ValueParameterDescriptor substitutedVersion = substitutedParametersMap.get(valueParameterDescriptor.getOriginal());
217                assert substitutedVersion != null : valueParameterDescriptor;
218                argumentToParameterMap.put(entry.getKey(), argumentMatch.replaceValueParameter(substitutedVersion));
219            }
220        }
221    
222        @Override
223        public void setConstraintSystem(@NotNull ConstraintSystem constraintSystem) {
224            this.constraintSystem = constraintSystem;
225        }
226    
227        @Nullable
228        @Override
229        public ConstraintSystem getConstraintSystem() {
230            assertNotCompleted("ConstraintSystem");
231            return constraintSystem;
232        }
233    
234        @Override
235        public void recordValueArgument(@NotNull ValueParameterDescriptor valueParameter, @NotNull ResolvedValueArgument valueArgument) {
236            assert !valueArguments.containsKey(valueParameter) : valueParameter + " -> " + valueArgument;
237            valueArguments.put(valueParameter, valueArgument);
238            for (ValueArgument argument : valueArgument.getArguments()) {
239                argumentToParameterMap.put(argument, new ArgumentMatchImpl(valueParameter));
240            }
241        }
242    
243        @Override
244        @Nullable
245        public Receiver getExtensionReceiver() {
246            return extensionReceiver;
247        }
248    
249        @Override
250        @Nullable
251        public ReceiverValue getDispatchReceiver() {
252            return dispatchReceiver;
253        }
254    
255        @Override
256        @NotNull
257        public ExplicitReceiverKind getExplicitReceiverKind() {
258            return explicitReceiverKind;
259        }
260    
261        @Override
262        @NotNull
263        public Map<ValueParameterDescriptor, ResolvedValueArgument> getValueArguments() {
264            return valueArguments;
265        }
266    
267        @Override
268        @NotNull
269        public Map<ValueParameterDescriptor, ResolvedValueArgument> getUnsubstitutedValueArguments() {
270            // TODO We need unsubstituted value arguments to compare signatures for specificity when explicit type arguments are provided.
271            // Current implementation is questionable (mostly due to lack of well-defined contract for MutableResolvedCall).
272            return valueArgumentsBeforeSubstitution != null ? valueArgumentsBeforeSubstitution : valueArguments;
273        }
274    
275        @Nullable
276        @Override
277        public List<ResolvedValueArgument> getValueArgumentsByIndex() {
278            List<ResolvedValueArgument> arguments = new ArrayList<ResolvedValueArgument>(candidateDescriptor.getValueParameters().size());
279            for (int i = 0; i < candidateDescriptor.getValueParameters().size(); ++i) {
280                arguments.add(null);
281            }
282            
283            for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : valueArguments.entrySet()) {
284                ValueParameterDescriptor parameterDescriptor = entry.getKey();
285                ResolvedValueArgument value = entry.getValue();
286                ResolvedValueArgument oldValue = arguments.set(parameterDescriptor.getIndex(), value);
287                if (oldValue != null) {
288                    return null;
289                }
290            }
291    
292            for (int i = 0; i < arguments.size(); i++) {
293                Object o = arguments.get(i);
294                if (o == null) {
295                    return null;
296                }
297            }
298            
299            return arguments;
300        }
301    
302        @Override
303        public void recordArgumentMatchStatus(@NotNull ValueArgument valueArgument, @NotNull ArgumentMatchStatus matchStatus) {
304            ArgumentMatchImpl argumentMatch = argumentToParameterMap.get(valueArgument);
305            argumentMatch.recordMatchStatus(matchStatus);
306        }
307    
308        @NotNull
309        @Override
310        public ArgumentMapping getArgumentMapping(@NotNull ValueArgument valueArgument) {
311            ArgumentMatch argumentMatch = argumentToParameterMap.get(valueArgument);
312            if (argumentMatch == null) {
313                if (ArgumentMappingKt.isReallySuccess(this)) {
314                    LOG.error("ArgumentUnmapped for " + valueArgument + " in successfully resolved call: " + call.getCallElement().getText());
315                }
316                return ArgumentUnmapped.INSTANCE;
317            }
318            return argumentMatch;
319        }
320    
321        @NotNull
322        @Override
323        public Map<TypeParameterDescriptor, KotlinType> getTypeArguments() {
324            return typeArguments;
325        }
326    
327        @Override
328        public boolean isSafeCall() {
329            return CallUtilKt.isSafeCall(call);
330        }
331    
332        @NotNull
333        @Override
334        public MutableDataFlowInfoForArguments getDataFlowInfoForArguments() {
335            return dataFlowInfoForArguments;
336        }
337    
338        @Override
339        public boolean hasInferredReturnType() {
340            if (!completed) {
341                hasInferredReturnType = constraintSystem == null ||
342                                        CallResolverUtilKt.hasInferredReturnType(candidateDescriptor, constraintSystem);
343            }
344            assert hasInferredReturnType != null : "The property 'hasInferredReturnType' was not set when the call was completed.";
345            return hasInferredReturnType;
346        }
347    
348        @Override
349        public void markCallAsCompleted() {
350            if (!completed) {
351                hasInferredReturnType();
352            }
353            trace = null;
354            constraintSystem = null;
355            tracing = null;
356            completed = true;
357        }
358    
359        @Override
360        public boolean isCompleted() {
361            return completed;
362        }
363    
364        private void assertNotCompleted(String elementName) {
365            assert !completed: elementName + " is erased after resolution completion.";
366        }
367    
368        @Override
369        @Nullable
370        public TypeSubstitutor getKnownTypeParametersSubstitutor() {
371            return knownTypeParametersSubstitutor;
372        }
373    }