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
017package org.jetbrains.jet.lang.types.expressions;
018
019import com.google.common.base.Function;
020import com.google.common.collect.Lists;
021import com.intellij.openapi.project.Project;
022import com.intellij.psi.PsiElement;
023import com.intellij.psi.tree.IElementType;
024import org.jetbrains.annotations.NotNull;
025import org.jetbrains.annotations.Nullable;
026import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
027import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
028import org.jetbrains.jet.lang.descriptors.impl.FunctionDescriptorUtil;
029import org.jetbrains.jet.lang.descriptors.ScriptDescriptor;
030import org.jetbrains.jet.lang.psi.*;
031import org.jetbrains.jet.lang.resolve.*;
032import org.jetbrains.jet.lang.resolve.calls.CallExpressionResolver;
033import org.jetbrains.jet.lang.resolve.calls.CallResolver;
034import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
035import org.jetbrains.jet.lang.resolve.calls.context.ExpressionPosition;
036import org.jetbrains.jet.lang.resolve.scopes.JetScope;
037import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
038import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
039import org.jetbrains.jet.lang.types.CommonSupertypes;
040import org.jetbrains.jet.lang.types.ErrorUtils;
041import org.jetbrains.jet.lang.types.JetType;
042import org.jetbrains.jet.lang.types.JetTypeInfo;
043import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
044import org.jetbrains.jet.lexer.JetTokens;
045
046import javax.inject.Inject;
047import java.util.*;
048
049import static org.jetbrains.jet.lang.resolve.BindingContext.LABEL_TARGET;
050import static org.jetbrains.jet.lang.resolve.BindingContext.STATEMENT;
051import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
052import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.makeTraceInterceptingTypeMismatch;
053
054public class ExpressionTypingServices {
055
056    private final ExpressionTypingFacade expressionTypingFacade = ExpressionTypingVisitorDispatcher.create();
057
058    @NotNull
059    private Project project;
060    @NotNull
061    private CallResolver callResolver;
062    @NotNull
063    private CallExpressionResolver callExpressionResolver;
064    @NotNull
065    private DescriptorResolver descriptorResolver;
066    @NotNull
067    private TypeResolver typeResolver;
068
069
070
071    @NotNull
072    public Project getProject() {
073        return project;
074    }
075
076    @Inject
077    public void setProject(@NotNull Project project) {
078        this.project = project;
079    }
080
081    @NotNull
082    public CallResolver getCallResolver() {
083        return callResolver;
084    }
085
086    @Inject
087    public void setCallResolver(@NotNull CallResolver callResolver) {
088        this.callResolver = callResolver;
089    }
090
091    @NotNull
092    public CallExpressionResolver getCallExpressionResolver() {
093        return callExpressionResolver;
094    }
095
096    @Inject
097    public void setCallExpressionResolver(@NotNull CallExpressionResolver callExpressionResolver) {
098        this.callExpressionResolver = callExpressionResolver;
099    }
100
101    @NotNull
102    public DescriptorResolver getDescriptorResolver() {
103        return descriptorResolver;
104    }
105
106    @Inject
107    public void setDescriptorResolver(@NotNull DescriptorResolver descriptorResolver) {
108        this.descriptorResolver = descriptorResolver;
109    }
110
111    @NotNull
112    public TypeResolver getTypeResolver() {
113        return typeResolver;
114    }
115
116    @Inject
117    public void setTypeResolver(@NotNull TypeResolver typeResolver) {
118        this.typeResolver = typeResolver;
119    }
120
121    @NotNull
122    public JetType safeGetType(@NotNull JetScope scope, @NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace) {
123        JetType type = getType(scope, expression, expectedType, dataFlowInfo, trace);
124        if (type != null) {
125            return type;
126        }
127        return ErrorUtils.createErrorType("Type for " + expression.getText());
128    }
129
130    @NotNull
131    public JetTypeInfo getTypeInfo(@NotNull JetScope scope, @NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace) {
132        ExpressionTypingContext context = ExpressionTypingContext.newContext(
133                this, trace, scope, dataFlowInfo, expectedType, ExpressionPosition.FREE
134        );
135        return expressionTypingFacade.getTypeInfo(expression, context);
136    }
137
138    @Nullable
139    public JetType getType(@NotNull JetScope scope, @NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace) {
140        return getTypeInfo(scope, expression, expectedType, dataFlowInfo, trace).getType();
141    }
142
143    public JetTypeInfo getTypeInfoWithNamespaces(@NotNull JetExpression expression, @NotNull JetScope scope, @NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace) {
144        ExpressionTypingContext context = ExpressionTypingContext.newContext(
145                this, trace, scope, dataFlowInfo, expectedType, ExpressionPosition.LHS_OF_DOT);
146        return expressionTypingFacade.getTypeInfo(expression, context);
147    }
148
149    @NotNull
150    public JetType inferFunctionReturnType(@NotNull JetScope outerScope, @NotNull JetDeclarationWithBody function, @NotNull FunctionDescriptor functionDescriptor, @NotNull BindingTrace trace) {
151        Map<JetExpression, JetType> typeMap = collectReturnedExpressionsWithTypes(trace, outerScope, function, functionDescriptor);
152        Collection<JetType> types = typeMap.values();
153        return types.isEmpty()
154               ? KotlinBuiltIns.getInstance().getNothingType()
155               : CommonSupertypes.commonSupertype(types);
156    }
157
158
159    /////////////////////////////////////////////////////////
160
161    public void checkFunctionReturnType(@NotNull JetScope functionInnerScope, @NotNull JetDeclarationWithBody function, @NotNull FunctionDescriptor functionDescriptor, @NotNull DataFlowInfo dataFlowInfo, @Nullable JetType expectedReturnType, BindingTrace trace) {
162        if (expectedReturnType == null) {
163            expectedReturnType = functionDescriptor.getReturnType();
164            if (!function.hasBlockBody() && !function.hasDeclaredReturnType()) {
165                expectedReturnType = NO_EXPECTED_TYPE;
166            }
167        }
168        checkFunctionReturnType(function, ExpressionTypingContext.newContext(
169                this, trace, functionInnerScope, dataFlowInfo, expectedReturnType != null ? expectedReturnType : NO_EXPECTED_TYPE, ExpressionPosition.FREE
170        ), trace);
171    }
172
173    /*package*/ void checkFunctionReturnType(JetDeclarationWithBody function, ExpressionTypingContext context, BindingTrace trace) {
174        JetExpression bodyExpression = function.getBodyExpression();
175        if (bodyExpression == null) return;
176
177        boolean blockBody = function.hasBlockBody();
178        ExpressionTypingContext newContext =
179                blockBody
180                ? context.replaceExpectedType(NO_EXPECTED_TYPE)
181                : context;
182
183        if (function instanceof JetFunctionLiteral) {
184            JetFunctionLiteral functionLiteral = (JetFunctionLiteral) function;
185            JetBlockExpression blockExpression = functionLiteral.getBodyExpression();
186            assert blockExpression != null;
187            getBlockReturnedType(newContext.scope, blockExpression, CoercionStrategy.COERCION_TO_UNIT, context, trace);
188        }
189        else {
190            expressionTypingFacade.getTypeInfo(bodyExpression, newContext, !blockBody);
191        }
192    }
193
194    @NotNull
195    public JetTypeInfo getBlockReturnedType(@NotNull JetScope outerScope, @NotNull JetBlockExpression expression, @NotNull CoercionStrategy coercionStrategyForLastExpression, ExpressionTypingContext context, BindingTrace trace) {
196        List<JetElement> block = expression.getStatements();
197
198        DeclarationDescriptor containingDescriptor = outerScope.getContainingDeclaration();
199        if (containingDescriptor instanceof ScriptDescriptor) {
200            if (!(expression.getParent() instanceof JetScript)) {
201                // top level script declarations should have ScriptDescriptor parent
202                // and lower level script declarations should be ScriptCodeDescriptor parent
203                containingDescriptor = ((ScriptDescriptor) containingDescriptor).getScriptCodeDescriptor();
204            }
205        }
206        WritableScope scope = new WritableScopeImpl(
207                outerScope, containingDescriptor, new TraceBasedRedeclarationHandler(context.trace), "getBlockReturnedType");
208        scope.changeLockLevel(WritableScope.LockLevel.BOTH);
209
210        JetTypeInfo r;
211        if (block.isEmpty()) {
212            r = DataFlowUtils.checkType(KotlinBuiltIns.getInstance().getUnitType(), expression, context, context.dataFlowInfo);
213        }
214        else {
215            r = getBlockReturnedTypeWithWritableScope(scope, block, coercionStrategyForLastExpression, context, trace);
216        }
217        scope.changeLockLevel(WritableScope.LockLevel.READING);
218
219        if (containingDescriptor instanceof ScriptDescriptor) {
220            trace.record(BindingContext.SCRIPT_SCOPE, (ScriptDescriptor) containingDescriptor, scope);
221        }
222
223        return r;
224    }
225
226    private Map<JetExpression, JetType> collectReturnedExpressionsWithTypes(
227            final @NotNull BindingTrace trace,
228            JetScope outerScope,
229            final JetDeclarationWithBody function,
230            FunctionDescriptor functionDescriptor) {
231        JetExpression bodyExpression = function.getBodyExpression();
232        assert bodyExpression != null;
233        JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, trace);
234        expressionTypingFacade.getTypeInfo(bodyExpression, ExpressionTypingContext.newContext(
235                this,
236                trace, functionInnerScope, DataFlowInfo.EMPTY, NO_EXPECTED_TYPE, ExpressionPosition.FREE), !function.hasBlockBody());
237        //todo function literals
238        final Collection<JetExpression> returnedExpressions = Lists.newArrayList();
239        if (function.hasBlockBody()) {
240            //now this code is never invoked!, it should be invoked for inference of return type of function literal with local returns
241            bodyExpression.accept(new JetTreeVisitor<JetDeclarationWithBody>() {
242                @Override
243                public Void visitReturnExpression(JetReturnExpression expression, JetDeclarationWithBody outerFunction) {
244                    JetSimpleNameExpression targetLabel = expression.getTargetLabel();
245                    PsiElement element = targetLabel != null ? trace.get(LABEL_TARGET, targetLabel) : null;
246                    if (element == function || (targetLabel == null && outerFunction == function)) {
247                        returnedExpressions.add(expression);
248                    }
249                    return null;
250                }
251
252                @Override
253                public Void visitFunctionLiteralExpression(JetFunctionLiteralExpression expression, JetDeclarationWithBody outerFunction) {
254                    return super.visitFunctionLiteralExpression(expression, expression.getFunctionLiteral());
255                }
256
257                @Override
258                public Void visitNamedFunction(JetNamedFunction function, JetDeclarationWithBody outerFunction) {
259                    return super.visitNamedFunction(function, function);
260                }
261            }, function);
262        }
263        else {
264            returnedExpressions.add(bodyExpression);
265        }
266        Map<JetExpression, JetType> typeMap = new HashMap<JetExpression, JetType>();
267        for (JetExpression returnedExpression : returnedExpressions) {
268            JetType cachedType = trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, returnedExpression);
269            trace.record(STATEMENT, returnedExpression, false);
270            if (cachedType != null) {
271                typeMap.put(returnedExpression, cachedType);
272            } 
273            else {
274                typeMap.put(returnedExpression, ErrorUtils.createErrorType("Error function type"));
275            }
276        }
277        return typeMap;
278    }
279
280    /*package*/
281    @SuppressWarnings("SuspiciousMethodCalls")
282    JetTypeInfo getBlockReturnedTypeWithWritableScope(@NotNull WritableScope scope, @NotNull List<? extends JetElement> block, @NotNull CoercionStrategy coercionStrategyForLastExpression, ExpressionTypingContext context, BindingTrace trace) {
283        if (block.isEmpty()) {
284            return JetTypeInfo.create(KotlinBuiltIns.getInstance().getUnitType(), context.dataFlowInfo);
285        }
286
287        ExpressionTypingInternals blockLevelVisitor = ExpressionTypingVisitorDispatcher.createForBlock(scope);
288        ExpressionTypingContext newContext = createContext(context, trace, scope, context.dataFlowInfo, NO_EXPECTED_TYPE);
289
290        JetTypeInfo result = JetTypeInfo.create(null, context.dataFlowInfo);
291        for (Iterator<? extends JetElement> iterator = block.iterator(); iterator.hasNext(); ) {
292            JetElement statement = iterator.next();
293            if (!(statement instanceof JetExpression)) {
294                continue;
295            }
296            trace.record(STATEMENT, statement);
297            JetExpression statementExpression = (JetExpression) statement;
298            //TODO constructor assert context.expectedType != FORBIDDEN : ""
299            if (!iterator.hasNext()) {
300                if (context.expectedType != NO_EXPECTED_TYPE) {
301                    if (coercionStrategyForLastExpression == CoercionStrategy.COERCION_TO_UNIT && KotlinBuiltIns.getInstance().isUnit(context.expectedType)) {
302                        // This implements coercion to Unit
303                        TemporaryBindingTrace temporaryTraceExpectingUnit = TemporaryBindingTrace.create(trace, "trace to resolve coercion to unit with expected type");
304                        boolean[] mismatch = new boolean[1];
305                        ObservableBindingTrace errorInterceptingTrace = makeTraceInterceptingTypeMismatch(temporaryTraceExpectingUnit, statementExpression, mismatch);
306                        newContext = createContext(newContext, errorInterceptingTrace, scope, newContext.dataFlowInfo, context.expectedType);
307                        result = blockLevelVisitor.getTypeInfo(statementExpression, newContext, true);
308                        if (mismatch[0]) {
309                            TemporaryBindingTrace temporaryTraceNoExpectedType = TemporaryBindingTrace.create(trace, "trace to resolve coercion to unit without expected type");
310                            mismatch[0] = false;
311                            ObservableBindingTrace interceptingTrace = makeTraceInterceptingTypeMismatch(temporaryTraceNoExpectedType, statementExpression, mismatch);
312                            newContext = createContext(newContext, interceptingTrace, scope, newContext.dataFlowInfo, NO_EXPECTED_TYPE);
313                            result = blockLevelVisitor.getTypeInfo(statementExpression, newContext, true);
314                            if (mismatch[0]) {
315                                temporaryTraceExpectingUnit.commit();
316                            }
317                            else {
318                                temporaryTraceNoExpectedType.commit();
319                            }
320                        }
321                        else {
322                            temporaryTraceExpectingUnit.commit();
323                        }
324                    }
325                    else {
326                        newContext = createContext(newContext, trace, scope, newContext.dataFlowInfo, context.expectedType);
327                        result = blockLevelVisitor.getTypeInfo(statementExpression, newContext, true);
328                    }
329                }
330                else {
331                    result = blockLevelVisitor.getTypeInfo(statementExpression, newContext, true);
332                    if (coercionStrategyForLastExpression == CoercionStrategy.COERCION_TO_UNIT) {
333                        boolean mightBeUnit = false;
334                        if (statementExpression instanceof JetDeclaration) {
335                            mightBeUnit = true;
336                        }
337                        if (statementExpression instanceof JetBinaryExpression) {
338                            JetBinaryExpression binaryExpression = (JetBinaryExpression) statementExpression;
339                            IElementType operationType = binaryExpression.getOperationToken();
340                            if (operationType == JetTokens.EQ || OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(operationType)) {
341                                mightBeUnit = true;
342                            }
343                        }
344                        if (mightBeUnit) {
345                            // ExpressionTypingVisitorForStatements should return only null or Unit for declarations and assignments
346                            assert result.getType() == null || KotlinBuiltIns.getInstance().isUnit(result.getType());
347                            result = JetTypeInfo.create(KotlinBuiltIns.getInstance().getUnitType(), newContext.dataFlowInfo);
348                        }
349                    }
350                }
351            }
352            else {
353                result = blockLevelVisitor.getTypeInfo(statementExpression, newContext, true);
354            }
355
356            DataFlowInfo newDataFlowInfo = result.getDataFlowInfo();
357            if (newDataFlowInfo != context.dataFlowInfo) {
358                newContext = createContext(newContext, trace, scope, newDataFlowInfo, NO_EXPECTED_TYPE);
359            }
360            blockLevelVisitor = ExpressionTypingVisitorDispatcher.createForBlock(scope);
361        }
362        return result;
363    }
364
365    private ExpressionTypingContext createContext(ExpressionTypingContext oldContext, BindingTrace trace, WritableScope scope, DataFlowInfo dataFlowInfo, JetType expectedType) {
366        return ExpressionTypingContext.newContext(
367                this, oldContext.labelResolver, trace, scope, dataFlowInfo, expectedType, oldContext.expressionPosition);
368    }
369
370    @Nullable
371    public JetExpression deparenthesize(
372            @NotNull JetExpression expression,
373            @NotNull final ExpressionTypingContext context) {
374        return JetPsiUtil.deparenthesizeWithResolutionStrategy(expression, new Function<JetTypeReference, Void>() {
375            @Override
376            public Void apply(JetTypeReference reference) {
377                getTypeResolver().resolveType(context.scope, reference, context.trace, true);
378                return null;
379            }
380        });
381    }
382}