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