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