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