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.types.expressions;
018    
019    import com.google.common.collect.Lists;
020    import com.google.common.collect.Maps;
021    import com.intellij.lang.ASTNode;
022    import com.intellij.openapi.diagnostic.Logger;
023    import com.intellij.openapi.util.Ref;
024    import com.intellij.psi.util.PsiTreeUtil;
025    import org.jetbrains.annotations.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    import org.jetbrains.kotlin.descriptors.*;
028    import org.jetbrains.kotlin.descriptors.annotations.Annotations;
029    import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl;
030    import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl;
031    import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
032    import org.jetbrains.kotlin.lexer.JetTokens;
033    import org.jetbrains.kotlin.name.Name;
034    import org.jetbrains.kotlin.psi.*;
035    import org.jetbrains.kotlin.resolve.BindingContextUtils;
036    import org.jetbrains.kotlin.resolve.BindingTrace;
037    import org.jetbrains.kotlin.resolve.calls.CallResolver;
038    import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem;
039    import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemStatus;
040    import org.jetbrains.kotlin.resolve.calls.inference.ConstraintsUtil;
041    import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData;
042    import org.jetbrains.kotlin.resolve.calls.model.MutableDataFlowInfoForArguments;
043    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
044    import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
045    import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
046    import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind;
047    import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionCandidate;
048    import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy;
049    import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
050    import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
051    import org.jetbrains.kotlin.types.*;
052    
053    import java.util.*;
054    
055    import static org.jetbrains.kotlin.resolve.BindingContext.CALL;
056    import static org.jetbrains.kotlin.resolve.BindingContext.RESOLVED_CALL;
057    import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.EXPECTED_TYPE_POSITION;
058    
059    public class ControlStructureTypingUtils {
060        private static final Logger LOG = Logger.getInstance(ControlStructureTypingUtils.class);
061    
062        private final CallResolver callResolver;
063    
064        public ControlStructureTypingUtils(@NotNull CallResolver callResolver) {
065            this.callResolver = callResolver;
066        }
067    
068        /*package*/ ResolvedCall<FunctionDescriptor> resolveSpecialConstructionAsCall(
069                @NotNull Call call,
070                @NotNull String constructionName,
071                @NotNull List<String> argumentNames,
072                @NotNull List<Boolean> isArgumentNullable,
073                @NotNull ExpressionTypingContext context,
074                @Nullable MutableDataFlowInfoForArguments dataFlowInfoForArguments
075        ) {
076            SimpleFunctionDescriptorImpl function = createFunctionDescriptorForSpecialConstruction(
077                    constructionName.toUpperCase(), argumentNames, isArgumentNullable);
078            TracingStrategy tracing = createTracingForSpecialConstruction(call, constructionName, context);
079            ResolutionCandidate<CallableDescriptor> resolutionCandidate = ResolutionCandidate.<CallableDescriptor>create(call, function);
080            OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveCallWithKnownCandidate(
081                    call, tracing, context, resolutionCandidate, dataFlowInfoForArguments);
082            assert results.isSingleResult() : "Not single result after resolving one known candidate";
083            return results.getResultingCall();
084        }
085    
086        private static SimpleFunctionDescriptorImpl createFunctionDescriptorForSpecialConstruction(
087                @NotNull String constructionName,
088                @NotNull List<String> argumentNames,
089                @NotNull List<Boolean> isArgumentNullable
090        ) {
091            assert argumentNames.size() == isArgumentNullable.size();
092    
093            Name specialFunctionName = Name.identifierNoValidate("<SPECIAL-FUNCTION-FOR-" + constructionName + "-RESOLVE>");
094    
095            SimpleFunctionDescriptorImpl function = SimpleFunctionDescriptorImpl.create(
096                    ErrorUtils.getErrorModule(),//todo hack to avoid returning true in 'isError(DeclarationDescriptor)'
097                    Annotations.EMPTY, specialFunctionName, CallableMemberDescriptor.Kind.DECLARATION, SourceElement.NO_SOURCE);
098    
099            TypeParameterDescriptor typeParameter = TypeParameterDescriptorImpl.createWithDefaultBound(
100                    function, Annotations.EMPTY, false, Variance.INVARIANT,
101                    Name.identifierNoValidate("<TYPE-PARAMETER-FOR-" + constructionName + "-RESOLVE>"), 0);
102    
103            JetType type = typeParameter.getDefaultType();
104            JetType nullableType = TypeUtils.makeNullable(type);
105    
106            List<ValueParameterDescriptor> valueParameters = new ArrayList<ValueParameterDescriptor>(argumentNames.size());
107            for (int i = 0; i < argumentNames.size(); i++) {
108                JetType argumentType = isArgumentNullable.get(i) ? nullableType : type;
109                ValueParameterDescriptorImpl valueParameter = new ValueParameterDescriptorImpl(
110                        function, null, i, Annotations.EMPTY, Name.identifier(argumentNames.get(i)),
111                        argumentType, false, null, SourceElement.NO_SOURCE
112                );
113                valueParameters.add(valueParameter);
114            }
115            function.initialize(
116                    null,
117                    ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER,
118                    Lists.newArrayList(typeParameter),
119                    valueParameters,
120                    type,
121                    Modality.FINAL,
122                    Visibilities.PUBLIC
123            );
124            return function;
125        }
126    
127        /*package*/ static MutableDataFlowInfoForArguments createIndependentDataFlowInfoForArgumentsForCall(
128                final Map<ValueArgument, DataFlowInfo> dataFlowInfoForArgumentsMap
129        ) {
130            return new MutableDataFlowInfoForArguments() {
131                private DataFlowInfo initialDataFlowInfo;
132    
133                @Override
134                public void setInitialDataFlowInfo(@NotNull DataFlowInfo dataFlowInfo) {
135                    this.initialDataFlowInfo = dataFlowInfo;
136                }
137    
138                @Override
139                public void updateInfo(@NotNull ValueArgument valueArgument, @NotNull DataFlowInfo dataFlowInfo) {
140                    //todo
141                }
142    
143                @NotNull
144                @Override
145                public DataFlowInfo getInfo(@NotNull ValueArgument valueArgument) {
146                    return dataFlowInfoForArgumentsMap.get(valueArgument);
147                }
148    
149                @NotNull
150                @Override
151                public DataFlowInfo getResultInfo() {
152                    //todo merge and use
153                    return initialDataFlowInfo;
154                }
155            };
156        }
157    
158        public static MutableDataFlowInfoForArguments createDataFlowInfoForArgumentsForIfCall(
159                @NotNull Call callForIf,
160                @NotNull DataFlowInfo thenInfo,
161                @NotNull DataFlowInfo elseInfo
162        ) {
163            Map<ValueArgument, DataFlowInfo> dataFlowInfoForArgumentsMap = Maps.newHashMap();
164            dataFlowInfoForArgumentsMap.put(callForIf.getValueArguments().get(0), thenInfo);
165            dataFlowInfoForArgumentsMap.put(callForIf.getValueArguments().get(1), elseInfo);
166            return createIndependentDataFlowInfoForArgumentsForCall(dataFlowInfoForArgumentsMap);
167        }
168    
169        /*package*/ static Call createCallForSpecialConstruction(
170                @NotNull final JetExpression expression,
171                @NotNull final JetExpression calleeExpression,
172                @NotNull List<? extends JetExpression> arguments
173        ) {
174            final List<ValueArgument> valueArguments = Lists.newArrayList();
175            for (JetExpression argument : arguments) {
176                valueArguments.add(CallMaker.makeValueArgument(argument));
177            }
178            return new Call() {
179                @Nullable
180                @Override
181                public ASTNode getCallOperationNode() {
182                    return expression.getNode();
183                }
184    
185                @NotNull
186                @Override
187                public ReceiverValue getExplicitReceiver() {
188                    return ReceiverValue.NO_RECEIVER;
189                }
190    
191                @NotNull
192                @Override
193                public ReceiverValue getDispatchReceiver() {
194                    return ReceiverValue.NO_RECEIVER;
195                }
196    
197                @Nullable
198                @Override
199                public JetExpression getCalleeExpression() {
200                    return calleeExpression;
201                }
202    
203                @Nullable
204                @Override
205                public JetValueArgumentList getValueArgumentList() {
206                    return null;
207                }
208    
209                @NotNull
210                @Override
211                public List<? extends ValueArgument> getValueArguments() {
212                    return valueArguments;
213                }
214    
215                @NotNull
216                @Override
217                public List<FunctionLiteralArgument> getFunctionLiteralArguments() {
218                    return Collections.emptyList();
219                }
220    
221                @NotNull
222                @Override
223                public List<JetTypeProjection> getTypeArguments() {
224                    return Collections.emptyList();
225                }
226    
227                @Nullable
228                @Override
229                public JetTypeArgumentList getTypeArgumentList() {
230                    return null;
231                }
232    
233                @NotNull
234                @Override
235                public JetElement getCallElement() {
236                    return expression;
237                }
238    
239                @NotNull
240                @Override
241                public CallType getCallType() {
242                    return CallType.DEFAULT;
243                }
244            };
245        }
246    
247        @NotNull
248        /*package*/ static TracingStrategy createTracingForSpecialConstruction(
249                final @NotNull Call call,
250                @NotNull String constructionName,
251                final @NotNull ExpressionTypingContext context
252        ) {
253            class CheckTypeContext {
254                public BindingTrace trace;
255                public JetType expectedType;
256    
257                CheckTypeContext(@NotNull BindingTrace trace, @NotNull JetType expectedType) {
258                    this.trace = trace;
259                    this.expectedType = expectedType;
260                }
261    
262                CheckTypeContext makeTypeNullable() {
263                    if (TypeUtils.noExpectedType(expectedType)) return this;
264                    return new CheckTypeContext(trace, TypeUtils.makeNullable(expectedType));
265                }
266            }
267    
268            final JetVisitor<Boolean, CheckTypeContext> checkTypeVisitor = new JetVisitor<Boolean, CheckTypeContext>() {
269    
270                private boolean checkExpressionType(@NotNull JetExpression expression, CheckTypeContext c) {
271                    JetTypeInfo typeInfo = BindingContextUtils.getRecordedTypeInfo(expression, c.trace.getBindingContext());
272                    if (typeInfo == null) return false;
273    
274                    Ref<Boolean> hasError = Ref.create();
275                    DataFlowUtils.checkType(
276                            typeInfo.getType(),
277                            expression,
278                            context
279                                    .replaceExpectedType(c.expectedType)
280                                    .replaceDataFlowInfo(typeInfo.getDataFlowInfo())
281                                    .replaceBindingTrace(c.trace),
282                            hasError
283                    );
284                    return hasError.get();
285                }
286    
287                private boolean checkExpressionTypeRecursively(@Nullable JetExpression expression, CheckTypeContext c) {
288                    if (expression == null) return false;
289                    return expression.accept(this, c);
290                }
291    
292                private boolean checkSubExpressions(
293                        JetExpression firstSub, JetExpression secondSub, JetExpression expression,
294                        CheckTypeContext firstContext, CheckTypeContext secondContext, CheckTypeContext context
295                ) {
296                    boolean errorWasReported = checkExpressionTypeRecursively(firstSub, firstContext);
297                    errorWasReported |= checkExpressionTypeRecursively(secondSub, secondContext);
298                    return errorWasReported || checkExpressionType(expression, context);
299                }
300    
301                @Override
302                public Boolean visitIfExpression(@NotNull JetIfExpression ifExpression, CheckTypeContext c) {
303                    JetExpression thenBranch = ifExpression.getThen();
304                    JetExpression elseBranch = ifExpression.getElse();
305                    if (thenBranch == null || elseBranch == null) {
306                        return checkExpressionType(ifExpression, c);
307                    }
308                    return checkSubExpressions(thenBranch, elseBranch, ifExpression, c, c, c);
309                }
310    
311                @Override
312                public Boolean visitBlockExpression(@NotNull JetBlockExpression expression, CheckTypeContext c) {
313                    if (expression.getStatements().isEmpty()) {
314                        return checkExpressionType(expression, c);
315                    }
316                    JetElement lastStatement = JetPsiUtil.getLastStatementInABlock(expression);
317                    if (lastStatement instanceof JetExpression) {
318                        return checkExpressionTypeRecursively((JetExpression) lastStatement, c);
319                    }
320                    return false;
321                }
322    
323                @Override
324                public Boolean visitPostfixExpression(@NotNull JetPostfixExpression expression, CheckTypeContext c) {
325                    if (expression.getOperationReference().getReferencedNameElementType() == JetTokens.EXCLEXCL) {
326                        return checkExpressionTypeRecursively(expression.getBaseExpression(), c.makeTypeNullable());
327                    }
328                    return super.visitPostfixExpression(expression, c);
329                }
330    
331                @Override
332                public Boolean visitBinaryExpression(@NotNull JetBinaryExpression expression, CheckTypeContext c) {
333                    if (expression.getOperationReference().getReferencedNameElementType() == JetTokens.ELVIS) {
334    
335                        return checkSubExpressions(expression.getLeft(), expression.getRight(), expression, c.makeTypeNullable(), c, c);
336                    }
337                    return super.visitBinaryExpression(expression, c);
338                }
339    
340                @Override
341                public Boolean visitExpression(@NotNull JetExpression expression, CheckTypeContext c) {
342                    return checkExpressionType(expression, c);
343                }
344            };
345    
346            return new ThrowingOnErrorTracingStrategy("resolve " + constructionName + " as a call") {
347                @Override
348                public <D extends CallableDescriptor> void bindReference(
349                        @NotNull BindingTrace trace, @NotNull ResolvedCall<D> resolvedCall
350                ) {
351                    //do nothing
352                }
353    
354                @Override
355                public void bindCall(@NotNull BindingTrace trace, @NotNull Call call) {
356                    trace.record(CALL, call.getCalleeExpression(), call);
357                }
358    
359                @Override
360                public <D extends CallableDescriptor> void bindResolvedCall(
361                        @NotNull BindingTrace trace, @NotNull ResolvedCall<D> resolvedCall
362                ) {
363                    trace.record(RESOLVED_CALL, call, resolvedCall);
364                }
365    
366                @Override
367                public void typeInferenceFailed(
368                        @NotNull BindingTrace trace, @NotNull InferenceErrorData data
369                ) {
370                    ConstraintSystem constraintSystem = data.constraintSystem;
371                    ConstraintSystemStatus status = constraintSystem.getStatus();
372                    assert !status.isSuccessful() : "Report error only for not successful constraint system";
373    
374                    if (status.hasErrorInConstrainingTypes() || status.hasUnknownParameters()) {
375                        return;
376                    }
377                    JetExpression expression = (JetExpression) call.getCallElement();
378                    if (status.hasOnlyErrorsFromPosition(EXPECTED_TYPE_POSITION.position()) || status.hasConflictingConstraints()) {
379                        expression.accept(checkTypeVisitor, new CheckTypeContext(trace, data.expectedType));
380                        return;
381                    }
382                    JetDeclaration parentDeclaration = PsiTreeUtil.getParentOfType(expression, JetNamedDeclaration.class);
383                    logError("Expression: " + (parentDeclaration != null ? parentDeclaration.getText() : expression.getText()) +
384                             "\nConstraint system status: \n" + ConstraintsUtil.getDebugMessageForStatus(status));
385                }
386            };
387        }
388        
389        private abstract static class ThrowingOnErrorTracingStrategy implements TracingStrategy {
390            private final String debugName;
391    
392            protected ThrowingOnErrorTracingStrategy(String debugName) {
393                this.debugName = debugName;
394            }
395    
396            private void logError() {
397                logError(null);
398            }
399    
400            protected void logError(@Nullable String additionalInformation) {
401                String errorMessage = "Resolution error of this type shouldn't occur for " + debugName;
402                if (additionalInformation != null) {
403                    errorMessage += ".\n" + additionalInformation;
404                }
405                LOG.error(errorMessage);
406            }
407    
408            @Override
409            public void unresolvedReference(@NotNull BindingTrace trace) {
410                logError();
411            }
412    
413            @Override
414            public <D extends CallableDescriptor> void unresolvedReferenceWrongReceiver(
415                    @NotNull BindingTrace trace, @NotNull Collection<? extends ResolvedCall<D>> candidates
416            ) {
417                logError();
418            }
419    
420            @Override
421            public <D extends CallableDescriptor> void recordAmbiguity(
422                    @NotNull BindingTrace trace, @NotNull Collection<? extends ResolvedCall<D>> candidates
423            ) {
424                logError();
425            }
426    
427            @Override
428            public void missingReceiver(
429                    @NotNull BindingTrace trace, @NotNull ReceiverParameterDescriptor expectedReceiver
430            ) {
431                logError();
432            }
433    
434            @Override
435            public void wrongReceiverType(
436                    @NotNull BindingTrace trace, @NotNull ReceiverParameterDescriptor receiverParameter, @NotNull ReceiverValue receiverArgument
437            ) {
438                logError();
439            }
440    
441            @Override
442            public void noReceiverAllowed(@NotNull BindingTrace trace) {
443                logError();
444            }
445    
446            @Override
447            public void noValueForParameter(
448                    @NotNull BindingTrace trace, @NotNull ValueParameterDescriptor valueParameter
449            ) {
450                logError();
451            }
452    
453            @Override
454            public void wrongNumberOfTypeArguments(@NotNull BindingTrace trace, int expectedTypeArgumentCount) {
455                logError();
456            }
457    
458            @Override
459            public <D extends CallableDescriptor> void ambiguity(
460                    @NotNull BindingTrace trace, @NotNull Collection<? extends ResolvedCall<D>> descriptors
461            ) {
462                logError();
463            }
464    
465            @Override
466            public <D extends CallableDescriptor> void noneApplicable(
467                    @NotNull BindingTrace trace, @NotNull Collection<? extends ResolvedCall<D>> descriptors
468            ) {
469                logError();
470            }
471    
472            @Override
473            public <D extends CallableDescriptor> void cannotCompleteResolve(
474                    @NotNull BindingTrace trace, @NotNull Collection<? extends ResolvedCall<D>> descriptors
475            ) {
476                logError();
477            }
478    
479            @Override
480            public void instantiationOfAbstractClass(@NotNull BindingTrace trace) {
481                logError();
482            }
483    
484            @Override
485            public void abstractSuperCall(@NotNull BindingTrace trace) {
486                logError();
487            }
488    
489            @Override
490            public void nestedClassAccessViaInstanceReference(
491                    @NotNull BindingTrace trace, @NotNull ClassDescriptor classDescriptor,
492                    @NotNull ExplicitReceiverKind explicitReceiverKind
493            ) {
494                logError();
495            }
496    
497            @Override
498            public void unsafeCall(
499                    @NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke
500            ) {
501                logError();
502            }
503    
504            @Override
505            public void unnecessarySafeCall(
506                    @NotNull BindingTrace trace, @NotNull JetType type
507            ) {
508                logError();
509            }
510    
511            @Override
512            public void invisibleMember(
513                    @NotNull BindingTrace trace, @NotNull DeclarationDescriptorWithVisibility descriptor
514            ) {
515                logError();
516            }
517    
518            @Override
519            public void typeInferenceFailed(
520                    @NotNull BindingTrace trace, @NotNull InferenceErrorData inferenceErrorData
521            ) {
522                logError();
523            }
524    
525            @Override
526            public void freeFunctionCalledAsExtension(@NotNull BindingTrace trace) {
527                logError();
528            }
529        }
530    }