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