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, SourceElement.NO_SOURCE);
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)),
103                        argumentType, false, null, SourceElement.NO_SOURCE
104                );
105                valueParameters.add(valueParameter);
106            }
107            function.initialize(
108                    null,
109                    ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER,
110                    Lists.newArrayList(typeParameter),
111                    valueParameters,
112                    type,
113                    Modality.FINAL,
114                    Visibilities.PUBLIC
115            );
116            return function;
117        }
118    
119        /*package*/ static MutableDataFlowInfoForArguments createIndependentDataFlowInfoForArgumentsForCall(
120                final Map<ValueArgument, DataFlowInfo> dataFlowInfoForArgumentsMap
121        ) {
122            return new MutableDataFlowInfoForArguments() {
123                private DataFlowInfo initialDataFlowInfo;
124    
125                @Override
126                public void setInitialDataFlowInfo(@NotNull DataFlowInfo dataFlowInfo) {
127                    this.initialDataFlowInfo = dataFlowInfo;
128                }
129    
130                @Override
131                public void updateInfo(@NotNull ValueArgument valueArgument, @NotNull DataFlowInfo dataFlowInfo) {
132                    //todo
133                }
134    
135                @NotNull
136                @Override
137                public DataFlowInfo getInfo(@NotNull ValueArgument valueArgument) {
138                    return dataFlowInfoForArgumentsMap.get(valueArgument);
139                }
140    
141                @NotNull
142                @Override
143                public DataFlowInfo getResultInfo() {
144                    //todo merge and use
145                    return initialDataFlowInfo;
146                }
147            };
148        }
149    
150        public static MutableDataFlowInfoForArguments createDataFlowInfoForArgumentsForIfCall(
151                @NotNull Call callForIf,
152                @NotNull DataFlowInfo thenInfo,
153                @NotNull DataFlowInfo elseInfo
154        ) {
155            Map<ValueArgument, DataFlowInfo> dataFlowInfoForArgumentsMap = Maps.newHashMap();
156            dataFlowInfoForArgumentsMap.put(callForIf.getValueArguments().get(0), thenInfo);
157            dataFlowInfoForArgumentsMap.put(callForIf.getValueArguments().get(1), elseInfo);
158            return createIndependentDataFlowInfoForArgumentsForCall(dataFlowInfoForArgumentsMap);
159        }
160    
161        /*package*/ static Call createCallForSpecialConstruction(
162                @NotNull final JetExpression expression,
163                @NotNull final JetExpression calleeExpression,
164                @NotNull List<? extends JetExpression> arguments
165        ) {
166            final List<ValueArgument> valueArguments = Lists.newArrayList();
167            for (JetExpression argument : arguments) {
168                valueArguments.add(CallMaker.makeValueArgument(argument));
169            }
170            return new Call() {
171                @Nullable
172                @Override
173                public ASTNode getCallOperationNode() {
174                    return expression.getNode();
175                }
176    
177                @NotNull
178                @Override
179                public ReceiverValue getExplicitReceiver() {
180                    return ReceiverValue.NO_RECEIVER;
181                }
182    
183                @NotNull
184                @Override
185                public ReceiverValue getThisObject() {
186                    return ReceiverValue.NO_RECEIVER;
187                }
188    
189                @Nullable
190                @Override
191                public JetExpression getCalleeExpression() {
192                    return calleeExpression;
193                }
194    
195                @Nullable
196                @Override
197                public JetValueArgumentList getValueArgumentList() {
198                    return null;
199                }
200    
201                @NotNull
202                @Override
203                public List<? extends ValueArgument> getValueArguments() {
204                    return valueArguments;
205                }
206    
207                @NotNull
208                @Override
209                public List<JetFunctionLiteralArgument> getFunctionLiteralArguments() {
210                    return Collections.emptyList();
211                }
212    
213                @NotNull
214                @Override
215                public List<JetTypeProjection> getTypeArguments() {
216                    return Collections.emptyList();
217                }
218    
219                @Nullable
220                @Override
221                public JetTypeArgumentList getTypeArgumentList() {
222                    return null;
223                }
224    
225                @NotNull
226                @Override
227                public JetElement getCallElement() {
228                    return expression;
229                }
230    
231                @NotNull
232                @Override
233                public CallType getCallType() {
234                    return CallType.DEFAULT;
235                }
236            };
237        }
238    
239        @NotNull
240        /*package*/ static TracingStrategy createTracingForSpecialConstruction(
241                final @NotNull Call call,
242                final @NotNull String constructionName
243        ) {
244            class CheckTypeContext {
245                public BindingTrace trace;
246                public JetType expectedType;
247    
248                CheckTypeContext(@NotNull BindingTrace trace, @NotNull JetType expectedType) {
249                    this.trace = trace;
250                    this.expectedType = expectedType;
251                }
252    
253                CheckTypeContext makeTypeNullable() {
254                    if (TypeUtils.noExpectedType(expectedType)) return this;
255                    return new CheckTypeContext(trace, TypeUtils.makeNullable(expectedType));
256                }
257            }
258    
259            final JetVisitor<Void, CheckTypeContext> checkTypeVisitor = new JetVisitor<Void, CheckTypeContext>() {
260                private void checkExpressionType(@Nullable JetExpression expression, CheckTypeContext c) {
261                    if (expression == null) return;
262                    expression.accept(this, c);
263                }
264    
265                @Override
266                public Void visitIfExpression(@NotNull JetIfExpression ifExpression, CheckTypeContext c) {
267                    JetExpression thenBranch = ifExpression.getThen();
268                    JetExpression elseBranch = ifExpression.getElse();
269                    if (thenBranch == null || elseBranch == null) {
270                        visitExpression(ifExpression, c);
271                        return null;
272                    }
273                    checkExpressionType(thenBranch, c);
274                    checkExpressionType(elseBranch, c);
275                    return null;
276                }
277    
278                @Override
279                public Void visitBlockExpression(@NotNull JetBlockExpression expression, CheckTypeContext c) {
280                    if (expression.getStatements().isEmpty()) {
281                        visitExpression(expression, c);
282                        return null;
283                    }
284                    JetElement lastStatement = JetPsiUtil.getLastStatementInABlock(expression);
285                    if (lastStatement instanceof JetExpression) {
286                        checkExpressionType((JetExpression) lastStatement, c);
287                    }
288                    return null;
289                }
290    
291                @Override
292                public Void visitPostfixExpression(@NotNull JetPostfixExpression expression, CheckTypeContext c) {
293                    if (expression.getOperationReference().getReferencedNameElementType() == JetTokens.EXCLEXCL) {
294                        checkExpressionType(expression.getBaseExpression(), c.makeTypeNullable());
295                        return null;
296                    }
297                    return super.visitPostfixExpression(expression, c);
298                }
299    
300                @Override
301                public Void visitBinaryExpression(@NotNull JetBinaryExpression expression, CheckTypeContext c) {
302                    if (expression.getOperationReference().getReferencedNameElementType() == JetTokens.ELVIS) {
303                        checkExpressionType(expression.getLeft(), c.makeTypeNullable());
304                        checkExpressionType(expression.getRight(), c);
305                        return null;
306                    }
307                    return super.visitBinaryExpression(expression, c);
308                }
309    
310                @Override
311                public Void visitExpression(@NotNull JetExpression expression, CheckTypeContext c) {
312                    JetTypeInfo typeInfo = BindingContextUtils.getRecordedTypeInfo(expression, c.trace.getBindingContext());
313                    if (typeInfo != null) {
314                        DataFlowUtils.checkType(typeInfo.getType(), expression, c.expectedType, typeInfo.getDataFlowInfo(), c.trace);
315                    }
316                    return null;
317                }
318            };
319    
320            return new ThrowingOnErrorTracingStrategy("resolve " + constructionName + " as a call") {
321                @Override
322                public <D extends CallableDescriptor> void bindReference(
323                        @NotNull BindingTrace trace, @NotNull ResolvedCall<D> resolvedCall
324                ) {
325                    //do nothing
326                }
327    
328                @Override
329                public void bindCall(@NotNull BindingTrace trace, @NotNull Call call) {
330                    trace.record(CALL, call.getCalleeExpression(), call);
331                }
332    
333                @Override
334                public <D extends CallableDescriptor> void bindResolvedCall(
335                        @NotNull BindingTrace trace, @NotNull ResolvedCall<D> resolvedCall
336                ) {
337                    trace.record(RESOLVED_CALL, call, resolvedCall);
338                }
339    
340                @Override
341                public void typeInferenceFailed(
342                        @NotNull BindingTrace trace, @NotNull InferenceErrorData data
343                ) {
344                    ConstraintSystem constraintSystem = data.constraintSystem;
345                    ConstraintSystemStatus status = constraintSystem.getStatus();
346                    assert !status.isSuccessful() : "Report error only for not successful constraint system";
347    
348                    if (status.hasErrorInConstrainingTypes()) {
349                        return;
350                    }
351                    JetExpression expression = (JetExpression) call.getCallElement();
352                    if (status.hasOnlyErrorsFromPosition(ConstraintPosition.EXPECTED_TYPE_POSITION) || status.hasConflictingConstraints()) {
353                        expression.accept(checkTypeVisitor, new CheckTypeContext(trace, data.expectedType));
354                        return;
355                    }
356                    throwError("Expression: " + expression.getText() + ".\nConstraint system status: \n" + ConstraintsUtil.getDebugMessageForStatus(status));
357                    super.typeInferenceFailed(trace, data);
358                }
359            };
360        }
361        
362        private abstract static class ThrowingOnErrorTracingStrategy implements TracingStrategy {
363            private final String debugName;
364    
365            protected ThrowingOnErrorTracingStrategy(String debugName) {
366                this.debugName = debugName;
367            }
368    
369            private void throwError() {
370                throwError(null);
371            }
372    
373            protected void throwError(@Nullable String additionalInformation) {
374                String errorMessage = "Resolution error of this type shouldn't occur for " + debugName;
375                if (additionalInformation != null) {
376                    errorMessage += ".\n" + additionalInformation;
377                }
378                throw new IllegalStateException(errorMessage);
379            }
380    
381            @Override
382            public void unresolvedReference(@NotNull BindingTrace trace) {
383                throwError();
384            }
385    
386            @Override
387            public <D extends CallableDescriptor> void unresolvedReferenceWrongReceiver(
388                    @NotNull BindingTrace trace, @NotNull Collection<? extends ResolvedCall<D>> candidates
389            ) {
390                throwError();
391            }
392    
393            @Override
394            public <D extends CallableDescriptor> void recordAmbiguity(
395                    @NotNull BindingTrace trace, @NotNull Collection<? extends ResolvedCall<D>> candidates
396            ) {
397                throwError();
398            }
399    
400            @Override
401            public void missingReceiver(
402                    @NotNull BindingTrace trace, @NotNull ReceiverParameterDescriptor expectedReceiver
403            ) {
404                throwError();
405            }
406    
407            @Override
408            public void wrongReceiverType(
409                    @NotNull BindingTrace trace, @NotNull ReceiverParameterDescriptor receiverParameter, @NotNull ReceiverValue receiverArgument
410            ) {
411                throwError();
412            }
413    
414            @Override
415            public void noReceiverAllowed(@NotNull BindingTrace trace) {
416                throwError();
417            }
418    
419            @Override
420            public void noValueForParameter(
421                    @NotNull BindingTrace trace, @NotNull ValueParameterDescriptor valueParameter
422            ) {
423                throwError();
424            }
425    
426            @Override
427            public void wrongNumberOfTypeArguments(@NotNull BindingTrace trace, int expectedTypeArgumentCount) {
428                throwError();
429            }
430    
431            @Override
432            public <D extends CallableDescriptor> void ambiguity(
433                    @NotNull BindingTrace trace, @NotNull Collection<? extends ResolvedCall<D>> descriptors
434            ) {
435                throwError();
436            }
437    
438            @Override
439            public <D extends CallableDescriptor> void noneApplicable(
440                    @NotNull BindingTrace trace, @NotNull Collection<? extends ResolvedCall<D>> descriptors
441            ) {
442                throwError();
443            }
444    
445            @Override
446            public <D extends CallableDescriptor> void cannotCompleteResolve(
447                    @NotNull BindingTrace trace, @NotNull Collection<? extends ResolvedCall<D>> descriptors
448            ) {
449                throwError();
450            }
451    
452            @Override
453            public void instantiationOfAbstractClass(@NotNull BindingTrace trace) {
454                throwError();
455            }
456    
457            @Override
458            public void unsafeCall(
459                    @NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke
460            ) {
461                throwError();
462            }
463    
464            @Override
465            public void unnecessarySafeCall(
466                    @NotNull BindingTrace trace, @NotNull JetType type
467            ) {
468                throwError();
469            }
470    
471            @Override
472            public void danglingFunctionLiteralArgumentSuspected(
473                    @NotNull BindingTrace trace, @NotNull List<JetFunctionLiteralArgument> functionLiteralArguments
474            ) {
475                throwError();
476            }
477    
478            @Override
479            public void invisibleMember(
480                    @NotNull BindingTrace trace, @NotNull DeclarationDescriptorWithVisibility descriptor
481            ) {
482                throwError();
483            }
484    
485            @Override
486            public void typeInferenceFailed(
487                    @NotNull BindingTrace trace, @NotNull InferenceErrorData inferenceErrorData
488            ) {
489                throwError();
490            }
491        }
492    }