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