001    /*
002     * Copyright 2010-2015 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.kotlin.types.expressions;
018    
019    import com.intellij.psi.tree.IElementType;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
023    import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
024    import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
025    import org.jetbrains.kotlin.descriptors.ScriptDescriptor;
026    import org.jetbrains.kotlin.lexer.JetTokens;
027    import org.jetbrains.kotlin.psi.*;
028    import org.jetbrains.kotlin.resolve.*;
029    import org.jetbrains.kotlin.resolve.calls.context.ContextDependency;
030    import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
031    import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
032    import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
033    import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope;
034    import org.jetbrains.kotlin.resolve.scopes.WritableScope;
035    import org.jetbrains.kotlin.resolve.scopes.utils.UtilsPackage;
036    import org.jetbrains.kotlin.types.ErrorUtils;
037    import org.jetbrains.kotlin.types.JetType;
038    import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryPackage;
039    
040    import java.util.Iterator;
041    import java.util.List;
042    
043    import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
044    import static org.jetbrains.kotlin.types.TypeUtils.UNIT_EXPECTED_TYPE;
045    import static org.jetbrains.kotlin.types.expressions.CoercionStrategy.COERCION_TO_UNIT;
046    
047    public class ExpressionTypingServices {
048    
049        private final ExpressionTypingFacade expressionTypingFacade;
050        private final ExpressionTypingComponents expressionTypingComponents;
051    
052        @NotNull private final AnnotationChecker annotationChecker;
053        @NotNull private final StatementFilter statementFilter;
054    
055        public ExpressionTypingServices(
056                @NotNull ExpressionTypingComponents components,
057                @NotNull AnnotationChecker annotationChecker,
058                @NotNull StatementFilter statementFilter,
059                @NotNull ExpressionTypingVisitorDispatcher.ForDeclarations facade
060        ) {
061            this.expressionTypingComponents = components;
062            this.annotationChecker = annotationChecker;
063            this.statementFilter = statementFilter;
064            this.expressionTypingFacade = facade;
065        }
066    
067        @NotNull public StatementFilter getStatementFilter() {
068            return statementFilter;
069        }
070    
071        @NotNull
072        public JetType safeGetType(
073                @NotNull LexicalScope scope,
074                @NotNull JetExpression expression,
075                @NotNull JetType expectedType,
076                @NotNull DataFlowInfo dataFlowInfo,
077                @NotNull BindingTrace trace
078        ) {
079            JetType type = getType(scope, expression, expectedType, dataFlowInfo, trace);
080    
081            return type != null ? type : ErrorUtils.createErrorType("Type for " + expression.getText());
082        }
083    
084        @NotNull
085        public JetTypeInfo getTypeInfo(
086                @NotNull LexicalScope scope,
087                @NotNull JetExpression expression,
088                @NotNull JetType expectedType,
089                @NotNull DataFlowInfo dataFlowInfo,
090                @NotNull BindingTrace trace,
091                boolean isStatement
092        ) {
093            ExpressionTypingContext context = ExpressionTypingContext.newContext(
094                    trace, scope, dataFlowInfo, expectedType
095            );
096            return expressionTypingFacade.getTypeInfo(expression, context, isStatement);
097        }
098    
099        @NotNull
100        public JetTypeInfo getTypeInfo(@NotNull JetExpression expression, @NotNull ResolutionContext resolutionContext) {
101            return expressionTypingFacade.getTypeInfo(expression, ExpressionTypingContext.newContext(resolutionContext));
102        }
103    
104        @Nullable
105        public JetType getType(
106                @NotNull LexicalScope scope,
107                @NotNull JetExpression expression,
108                @NotNull JetType expectedType,
109                @NotNull DataFlowInfo dataFlowInfo,
110                @NotNull BindingTrace trace
111        ) {
112            return getTypeInfo(scope, expression, expectedType, dataFlowInfo, trace, false).getType();
113        }
114    
115        /////////////////////////////////////////////////////////
116    
117        public void checkFunctionReturnType(
118                @NotNull LexicalScope functionInnerScope,
119                @NotNull JetDeclarationWithBody function,
120                @NotNull FunctionDescriptor functionDescriptor,
121                @NotNull DataFlowInfo dataFlowInfo,
122                @Nullable JetType expectedReturnType,
123                BindingTrace trace
124        ) {
125            if (expectedReturnType == null) {
126                expectedReturnType = functionDescriptor.getReturnType();
127                if (!function.hasBlockBody() && !function.hasDeclaredReturnType()) {
128                    expectedReturnType = NO_EXPECTED_TYPE;
129                }
130            }
131            checkFunctionReturnType(function, ExpressionTypingContext.newContext(
132                    trace,
133                    functionInnerScope, dataFlowInfo, expectedReturnType != null ? expectedReturnType : NO_EXPECTED_TYPE
134            ));
135        }
136    
137        /*package*/ void checkFunctionReturnType(JetDeclarationWithBody function, ExpressionTypingContext context) {
138            JetExpression bodyExpression = function.getBodyExpression();
139            if (bodyExpression == null) return;
140    
141            boolean blockBody = function.hasBlockBody();
142            ExpressionTypingContext newContext =
143                    blockBody
144                    ? context.replaceExpectedType(NO_EXPECTED_TYPE)
145                    : context;
146    
147            expressionTypingFacade.getTypeInfo(bodyExpression, newContext, blockBody);
148        }
149    
150        @NotNull
151        public JetTypeInfo getBlockReturnedType(JetBlockExpression expression, ExpressionTypingContext context, boolean isStatement) {
152            return getBlockReturnedType(expression, isStatement ? CoercionStrategy.COERCION_TO_UNIT : CoercionStrategy.NO_COERCION, context);
153        }
154    
155        @NotNull
156        public JetTypeInfo getBlockReturnedType(
157                @NotNull JetBlockExpression expression,
158                @NotNull CoercionStrategy coercionStrategyForLastExpression,
159                @NotNull ExpressionTypingContext context
160        ) {
161            List<JetExpression> block = ResolvePackage.filterStatements(statementFilter, expression);
162    
163            // SCRIPT: get code descriptor for script declaration
164            DeclarationDescriptor containingDescriptor = context.scope.getOwnerDescriptor();
165            if (containingDescriptor instanceof ScriptDescriptor) {
166                if (!(expression.getParent() instanceof JetScript)) {
167                    // top level script declarations should have ScriptDescriptor parent
168                    // and lower level script declarations should be ScriptCodeDescriptor parent
169                    containingDescriptor = ((ScriptDescriptor) containingDescriptor).getScriptCodeDescriptor();
170                }
171            }
172            LexicalWritableScope scope = new LexicalWritableScope(context.scope, containingDescriptor, false, null,
173                                                                  new TraceBasedRedeclarationHandler(context.trace), "getBlockReturnedType");
174            scope.changeLockLevel(WritableScope.LockLevel.BOTH);
175    
176            JetTypeInfo r;
177            if (block.isEmpty()) {
178                r = expressionTypingComponents.dataFlowAnalyzer
179                        .createCheckedTypeInfo(expressionTypingComponents.builtIns.getUnitType(), context, expression);
180            }
181            else {
182                r = getBlockReturnedTypeWithWritableScope(scope, block, coercionStrategyForLastExpression,
183                                                          context.replaceStatementFilter(statementFilter));
184            }
185            scope.changeLockLevel(WritableScope.LockLevel.READING);
186    
187            if (containingDescriptor instanceof ScriptDescriptor) {
188                context.trace.record(BindingContext.SCRIPT_SCOPE, (ScriptDescriptor) containingDescriptor, UtilsPackage.asJetScope(scope));
189            }
190    
191            return r;
192        }
193    
194        @NotNull
195        public JetType getBodyExpressionType(
196                @NotNull BindingTrace trace,
197                @NotNull LexicalScope outerScope,
198                @NotNull DataFlowInfo dataFlowInfo,
199                @NotNull JetDeclarationWithBody function,
200                @NotNull FunctionDescriptor functionDescriptor
201        ) {
202            JetExpression bodyExpression = function.getBodyExpression();
203            assert bodyExpression != null;
204            LexicalScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, trace);
205    
206            ExpressionTypingContext context = ExpressionTypingContext.newContext(
207                    trace, functionInnerScope, dataFlowInfo, NO_EXPECTED_TYPE
208            );
209            JetTypeInfo typeInfo = expressionTypingFacade.getTypeInfo(bodyExpression, context, function.hasBlockBody());
210    
211            JetType type = typeInfo.getType();
212            if (type != null) {
213                return type;
214            }
215            else {
216                return ErrorUtils.createErrorType("Error function type");
217            }
218        }
219    
220        /**
221         * Visits block statements propagating data flow information from the first to the last.
222         * Determines block returned type and data flow information at the end of the block AND
223         * at the nearest jump point from the block beginning.
224         */
225        /*package*/ JetTypeInfo getBlockReturnedTypeWithWritableScope(
226                @NotNull LexicalWritableScope scope,
227                @NotNull List<? extends JetElement> block,
228                @NotNull CoercionStrategy coercionStrategyForLastExpression,
229                @NotNull ExpressionTypingContext context
230        ) {
231            if (block.isEmpty()) {
232                return TypeInfoFactoryPackage.createTypeInfo(expressionTypingComponents.builtIns.getUnitType(), context);
233            }
234    
235            ExpressionTypingInternals blockLevelVisitor = new ExpressionTypingVisitorDispatcher.ForBlock(
236                    expressionTypingComponents, annotationChecker, scope);
237            ExpressionTypingContext newContext = context.replaceScope(scope).replaceExpectedType(NO_EXPECTED_TYPE);
238    
239            JetTypeInfo result = TypeInfoFactoryPackage.noTypeInfo(context);
240            // Jump point data flow info
241            DataFlowInfo beforeJumpInfo = newContext.dataFlowInfo;
242            boolean jumpOutPossible = false;
243            for (Iterator<? extends JetElement> iterator = block.iterator(); iterator.hasNext(); ) {
244                JetElement statement = iterator.next();
245                if (!(statement instanceof JetExpression)) {
246                    continue;
247                }
248                JetExpression statementExpression = (JetExpression) statement;
249                if (!iterator.hasNext()) {
250                    result = getTypeOfLastExpressionInBlock(
251                            statementExpression, newContext.replaceExpectedType(context.expectedType), coercionStrategyForLastExpression,
252                            blockLevelVisitor);
253                }
254                else {
255                    result = blockLevelVisitor
256                            .getTypeInfo(statementExpression, newContext.replaceContextDependency(ContextDependency.INDEPENDENT), true);
257                }
258    
259                DataFlowInfo newDataFlowInfo = result.getDataFlowInfo();
260                // If jump is not possible, we take new data flow info before jump
261                if (!jumpOutPossible) {
262                    beforeJumpInfo = result.getJumpFlowInfo();
263                    jumpOutPossible = result.getJumpOutPossible();
264                }
265                if (newDataFlowInfo != context.dataFlowInfo) {
266                    newContext = newContext.replaceDataFlowInfo(newDataFlowInfo);
267                    // We take current data flow info if jump there is not possible
268                }
269                blockLevelVisitor = new ExpressionTypingVisitorDispatcher.ForBlock(expressionTypingComponents, annotationChecker, scope);
270            }
271            return result.replaceJumpOutPossible(jumpOutPossible).replaceJumpFlowInfo(beforeJumpInfo);
272        }
273    
274        private JetTypeInfo getTypeOfLastExpressionInBlock(
275                @NotNull JetExpression statementExpression,
276                @NotNull ExpressionTypingContext context,
277                @NotNull CoercionStrategy coercionStrategyForLastExpression,
278                @NotNull ExpressionTypingInternals blockLevelVisitor
279        ) {
280            if (context.expectedType != NO_EXPECTED_TYPE) {
281                JetType expectedType;
282                if (context.expectedType == UNIT_EXPECTED_TYPE ||//the first check is necessary to avoid invocation 'isUnit(UNIT_EXPECTED_TYPE)'
283                    (coercionStrategyForLastExpression == COERCION_TO_UNIT && KotlinBuiltIns.isUnit(context.expectedType))) {
284                    expectedType = UNIT_EXPECTED_TYPE;
285                }
286                else {
287                    expectedType = context.expectedType;
288                }
289    
290                return blockLevelVisitor.getTypeInfo(statementExpression, context.replaceExpectedType(expectedType), true);
291            }
292            JetTypeInfo result = blockLevelVisitor.getTypeInfo(statementExpression, context, true);
293            if (coercionStrategyForLastExpression == COERCION_TO_UNIT) {
294                boolean mightBeUnit = false;
295                if (statementExpression instanceof JetDeclaration) {
296                    mightBeUnit = true;
297                }
298                if (statementExpression instanceof JetBinaryExpression) {
299                    JetBinaryExpression binaryExpression = (JetBinaryExpression) statementExpression;
300                    IElementType operationType = binaryExpression.getOperationToken();
301                    //noinspection SuspiciousMethodCalls
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                    // but (for correct assignment / initialization analysis) data flow info must be preserved
309                    assert result.getType() == null || KotlinBuiltIns.isUnit(result.getType());
310                    result = result.replaceType(expressionTypingComponents.builtIns.getUnitType());
311                }
312            }
313            return result;
314        }
315    
316    }