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