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