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 org.jetbrains.annotations.NotNull;
020import org.jetbrains.annotations.Nullable;
021import org.jetbrains.jet.lang.psi.*;
022import org.jetbrains.jet.lang.resolve.BindingContext;
023import org.jetbrains.jet.lang.resolve.BindingContextUtils;
024import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
025import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
026import org.jetbrains.jet.lang.types.DeferredType;
027import org.jetbrains.jet.lang.types.ErrorUtils;
028import org.jetbrains.jet.lang.types.JetTypeInfo;
029import org.jetbrains.jet.util.lazy.ReenteringLazyValueComputationException;
030
031import static org.jetbrains.jet.lang.diagnostics.Errors.TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM;
032
033public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetTypeInfo, ExpressionTypingContext> implements ExpressionTypingInternals {
034
035    @Override
036    public JetTypeInfo visitIdeTemplate(JetIdeTemplate expression, ExpressionTypingContext data) {
037        return basic.visitIdeTemplate(expression, data);
038    }
039
040    @NotNull
041    public static ExpressionTypingFacade create() {
042        return new ExpressionTypingVisitorDispatcher(null);
043    }
044
045    @NotNull
046    public static ExpressionTypingInternals createForBlock(WritableScope writableScope) {
047        return new ExpressionTypingVisitorDispatcher(writableScope);
048    }
049
050    private final BasicExpressionTypingVisitor basic;
051    private final ExpressionTypingVisitorForStatements statements;
052    private final ClosureExpressionsTypingVisitor closures = new ClosureExpressionsTypingVisitor(this);
053    private final ControlStructureTypingVisitor controlStructures = new ControlStructureTypingVisitor(this);
054    private final PatternMatchingTypingVisitor patterns = new PatternMatchingTypingVisitor(this);
055
056    private ExpressionTypingVisitorDispatcher(WritableScope writableScope) {
057        this.basic = new BasicExpressionTypingVisitor(this);
058        if (writableScope != null) {
059            this.statements = new ExpressionTypingVisitorForStatements(this, writableScope, basic, controlStructures, patterns);
060        }
061        else {
062            this.statements = null;
063        }
064    }
065
066    @NotNull
067    @Override
068    public JetTypeInfo checkInExpression(JetElement callElement, @NotNull JetSimpleNameExpression operationSign, @Nullable JetExpression left, @NotNull JetExpression right, ExpressionTypingContext context) {
069        return basic.checkInExpression(callElement, operationSign, left, right, context);
070    }
071
072    @Override
073    @NotNull
074    public final JetTypeInfo safeGetTypeInfo(@NotNull JetExpression expression, ExpressionTypingContext context) {
075        JetTypeInfo typeInfo = getTypeInfo(expression, context);
076        if (typeInfo.getType() != null) {
077            return typeInfo;
078        }
079        return JetTypeInfo.create(ErrorUtils.createErrorType("Type for " + expression.getText()), context.dataFlowInfo);
080    }
081
082    @Override
083    @NotNull
084    public final JetTypeInfo getTypeInfo(@NotNull JetExpression expression, ExpressionTypingContext context) {
085        return getTypeInfo(expression, context, this);
086    }
087
088    @Override
089    @NotNull
090    public final JetTypeInfo getTypeInfo(@NotNull JetExpression expression, ExpressionTypingContext context, boolean isStatement) {
091        if (!isStatement) return getTypeInfo(expression, context);
092        if (statements != null) {
093            return getTypeInfo(expression, context, statements);
094        }
095        return getTypeInfo(expression, context, createStatementVisitor(context));
096    }
097    
098    private ExpressionTypingVisitorForStatements createStatementVisitor(ExpressionTypingContext context) {
099        return new ExpressionTypingVisitorForStatements(this, ExpressionTypingUtils.newWritableScopeImpl(context, "statement scope"), basic, controlStructures, patterns);
100    }
101
102    @Override
103    public void checkStatementType(@NotNull JetExpression expression, ExpressionTypingContext context) {
104        expression.accept(createStatementVisitor(context), context);
105    }
106
107    @NotNull
108    private JetTypeInfo getTypeInfo(@NotNull JetExpression expression, ExpressionTypingContext context, JetVisitor<JetTypeInfo, ExpressionTypingContext> visitor) {
109        JetTypeInfo recordedTypeInfo = BindingContextUtils.getRecordedTypeInfo(expression, context.trace.getBindingContext());
110        if (recordedTypeInfo != null) {
111            return recordedTypeInfo;
112        }
113        JetTypeInfo result;
114        try {
115            result = expression.accept(visitor, context);
116            // Some recursive definitions (object expressions) must put their types in the cache manually:
117            if (context.trace.get(BindingContext.PROCESSED, expression)) {
118                return JetTypeInfo.create(context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression),
119                                          result.getDataFlowInfo());
120            }
121
122            if (result.getType() instanceof DeferredType) {
123                result = JetTypeInfo.create(((DeferredType) result.getType()).getActualType(), result.getDataFlowInfo());
124            }
125            if (result.getType() != null) {
126                context.trace.record(BindingContext.EXPRESSION_TYPE, expression, result.getType());
127            }
128
129        }
130        catch (ReenteringLazyValueComputationException e) {
131            context.trace.report(TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM.on(expression));
132            result = JetTypeInfo.create(null, context.dataFlowInfo);
133        }
134
135        if (!context.trace.get(BindingContext.PROCESSED, expression) && !BindingContextUtils.isExpressionWithValidReference(expression, context.trace.getBindingContext())) {
136            context.trace.record(BindingContext.RESOLUTION_SCOPE, expression, context.scope);
137        }
138        context.trace.record(BindingContext.PROCESSED, expression);
139        if (result.getDataFlowInfo() != DataFlowInfo.EMPTY) {
140            context.trace.record(BindingContext.EXPRESSION_DATA_FLOW_INFO, expression, result.getDataFlowInfo());
141        }
142        return result;
143    }  
144
145    //////////////////////////////////////////////////////////////////////////////////////////////
146
147    @Override
148    public JetTypeInfo visitFunctionLiteralExpression(JetFunctionLiteralExpression expression, ExpressionTypingContext data) {
149        return expression.accept(closures, data);
150    }
151
152    @Override
153    public JetTypeInfo visitObjectLiteralExpression(JetObjectLiteralExpression expression, ExpressionTypingContext data) {
154        return expression.accept(closures, data);
155    }
156
157//////////////////////////////////////////////////////////////////////////////////////////////
158
159    @Override
160    public JetTypeInfo visitThrowExpression(JetThrowExpression expression, ExpressionTypingContext data) {
161        return expression.accept(controlStructures, data);
162    }
163
164    @Override
165    public JetTypeInfo visitReturnExpression(JetReturnExpression expression, ExpressionTypingContext data) {
166        return expression.accept(controlStructures, data);
167    }
168
169    @Override
170    public JetTypeInfo visitContinueExpression(JetContinueExpression expression, ExpressionTypingContext data) {
171        return expression.accept(controlStructures, data);
172    }
173
174    @Override
175    public JetTypeInfo visitIfExpression(JetIfExpression expression, ExpressionTypingContext data) {
176        return expression.accept(controlStructures, data);
177    }
178
179    @Override
180    public JetTypeInfo visitTryExpression(JetTryExpression expression, ExpressionTypingContext data) {
181        return expression.accept(controlStructures, data);
182    }
183
184    @Override
185    public JetTypeInfo visitForExpression(JetForExpression expression, ExpressionTypingContext data) {
186        return expression.accept(controlStructures, data);
187    }
188
189    @Override
190    public JetTypeInfo visitWhileExpression(JetWhileExpression expression, ExpressionTypingContext data) {
191        return expression.accept(controlStructures, data);
192    }
193
194    @Override
195    public JetTypeInfo visitDoWhileExpression(JetDoWhileExpression expression, ExpressionTypingContext data) {
196        return expression.accept(controlStructures, data);
197    }
198
199    @Override
200    public JetTypeInfo visitBreakExpression(JetBreakExpression expression, ExpressionTypingContext data) {
201        return expression.accept(controlStructures, data);
202    }
203
204//////////////////////////////////////////////////////////////////////////////////////////////
205
206    @Override
207    public JetTypeInfo visitIsExpression(JetIsExpression expression, ExpressionTypingContext data) {
208        return expression.accept(patterns, data);
209    }
210
211    @Override
212    public JetTypeInfo visitWhenExpression(JetWhenExpression expression, ExpressionTypingContext data) {
213        return expression.accept(patterns, data);
214    }
215
216//////////////////////////////////////////////////////////////////////////////////////////////
217
218    @Override
219    public JetTypeInfo visitJetElement(JetElement element, ExpressionTypingContext data) {
220        return element.accept(basic, data);
221    }
222}