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.base.Function;
020    import com.google.common.collect.Lists;
021    import com.intellij.openapi.project.Project;
022    import com.intellij.psi.PsiElement;
023    import com.intellij.psi.tree.IElementType;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.annotations.Nullable;
026    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
027    import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
028    import org.jetbrains.jet.lang.descriptors.impl.FunctionDescriptorUtil;
029    import org.jetbrains.jet.lang.descriptors.ScriptDescriptor;
030    import org.jetbrains.jet.lang.psi.*;
031    import org.jetbrains.jet.lang.resolve.*;
032    import org.jetbrains.jet.lang.resolve.calls.CallExpressionResolver;
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.context.ExpressionPosition;
036    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
037    import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
038    import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
039    import org.jetbrains.jet.lang.types.CommonSupertypes;
040    import org.jetbrains.jet.lang.types.ErrorUtils;
041    import org.jetbrains.jet.lang.types.JetType;
042    import org.jetbrains.jet.lang.types.JetTypeInfo;
043    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
044    import org.jetbrains.jet.lexer.JetTokens;
045    
046    import javax.inject.Inject;
047    import java.util.*;
048    
049    import static org.jetbrains.jet.lang.resolve.BindingContext.LABEL_TARGET;
050    import static org.jetbrains.jet.lang.resolve.BindingContext.STATEMENT;
051    import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
052    import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.makeTraceInterceptingTypeMismatch;
053    
054    public 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    }