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.intellij.openapi.util.Ref;
020    import com.intellij.psi.tree.IElementType;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.lang.evaluate.ConstantExpressionEvaluator;
024    import org.jetbrains.jet.lang.evaluate.EvaluatePackage;
025    import org.jetbrains.jet.lang.psi.*;
026    import org.jetbrains.jet.lang.resolve.BindingContext;
027    import org.jetbrains.jet.lang.resolve.BindingTrace;
028    import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastUtils;
029    import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
030    import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue;
031    import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
032    import org.jetbrains.jet.lang.resolve.calls.context.ResolutionContext;
033    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
034    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstantChecker;
035    import org.jetbrains.jet.lang.resolve.constants.IntegerValueTypeConstant;
036    import org.jetbrains.jet.lang.types.JetType;
037    import org.jetbrains.jet.lang.types.JetTypeInfo;
038    import org.jetbrains.jet.lang.types.TypeUtils;
039    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
040    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
041    import org.jetbrains.jet.lexer.JetTokens;
042    
043    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
044    import static org.jetbrains.jet.lang.resolve.calls.context.ContextDependency.INDEPENDENT;
045    import static org.jetbrains.jet.lang.types.TypeUtils.*;
046    
047    public class DataFlowUtils {
048        private DataFlowUtils() {
049        }
050    
051        @NotNull
052        public static DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, final ExpressionTypingContext context) {
053            if (condition == null) return context.dataFlowInfo;
054            final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(null);
055            condition.accept(new JetVisitorVoid() {
056                @Override
057                public void visitIsExpression(@NotNull JetIsExpression expression) {
058                    if (conditionValue && !expression.isNegated() || !conditionValue && expression.isNegated()) {
059                        result.set(context.trace.get(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression));
060                    }
061                }
062    
063                @Override
064                public void visitBinaryExpression(@NotNull JetBinaryExpression expression) {
065                    IElementType operationToken = expression.getOperationToken();
066                    if (OperatorConventions.BOOLEAN_OPERATIONS.containsKey(operationToken)) {
067                        DataFlowInfo dataFlowInfo = extractDataFlowInfoFromCondition(expression.getLeft(), conditionValue, context);
068                        JetExpression expressionRight = expression.getRight();
069                        if (expressionRight != null) {
070                            DataFlowInfo rightInfo = extractDataFlowInfoFromCondition(expressionRight, conditionValue, context);
071                            boolean and = operationToken == JetTokens.ANDAND;
072                            if (and == conditionValue) { // this means: and && conditionValue || !and && !conditionValue
073                                dataFlowInfo = dataFlowInfo.and(rightInfo);
074                            }
075                            else {
076                                dataFlowInfo = dataFlowInfo.or(rightInfo);
077                            }
078                        }
079                        result.set(dataFlowInfo);
080                    }
081                    else  {
082                        JetExpression left = expression.getLeft();
083                        if (left == null) return;
084                        JetExpression right = expression.getRight();
085                        if (right == null) return;
086    
087                        JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left);
088                        if (lhsType == null) return;
089                        JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right);
090                        if (rhsType == null) return;
091    
092                        BindingContext bindingContext = context.trace.getBindingContext();
093                        DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, lhsType, bindingContext);
094                        DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rhsType, bindingContext);
095    
096                        Boolean equals = null;
097                        if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
098                            equals = true;
099                        }
100                        else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) {
101                            equals = false;
102                        }
103                        if (equals != null) {
104                            if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
105                                result.set(context.dataFlowInfo.equate(leftValue, rightValue));
106                            }
107                            else {
108                                result.set(context.dataFlowInfo.disequate(leftValue, rightValue));
109                            }
110    
111                        }
112                    }
113                }
114    
115                @Override
116                public void visitUnaryExpression(@NotNull JetUnaryExpression expression) {
117                    IElementType operationTokenType = expression.getOperationReference().getReferencedNameElementType();
118                    if (operationTokenType == JetTokens.EXCL) {
119                        JetExpression baseExpression = expression.getBaseExpression();
120                        if (baseExpression != null) {
121                            result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, context));
122                        }
123                    }
124                }
125    
126                @Override
127                public void visitParenthesizedExpression(@NotNull JetParenthesizedExpression expression) {
128                    JetExpression body = expression.getExpression();
129                    if (body != null) {
130                        body.accept(this);
131                    }
132                }
133            });
134            if (result.get() == null) {
135                return context.dataFlowInfo;
136            }
137            return context.dataFlowInfo.and(result.get());
138        }
139    
140        @NotNull
141        public static JetTypeInfo checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context, @NotNull DataFlowInfo dataFlowInfo) {
142            return JetTypeInfo.create(checkType(expressionType, expression, context), dataFlowInfo);
143        }
144    
145        @NotNull
146        public static JetTypeInfo checkType(@NotNull JetTypeInfo typeInfo, @NotNull JetExpression expression, @NotNull ResolutionContext context) {
147            JetType type = checkType(typeInfo.getType(), expression, context);
148            if (type == typeInfo.getType()) {
149                return typeInfo;
150            }
151            return JetTypeInfo.create(type, typeInfo.getDataFlowInfo());
152        }
153    
154        @Nullable
155        public static JetType checkType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ResolutionContext context) {
156            return checkType(expressionType, expression, context.expectedType, context.dataFlowInfo, context.trace);
157        }
158    
159        @Nullable
160        public static JetType checkType(@Nullable JetType expressionType, @NotNull JetExpression expressionToCheck,
161                @NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace
162        ) {
163            JetExpression expression = JetPsiUtil.safeDeparenthesize(expressionToCheck, false);
164            recordExpectedType(trace, expression, expectedType);
165    
166            if (expressionType == null || noExpectedType(expectedType) || !expectedType.getConstructor().isDenotable() ||
167                JetTypeChecker.DEFAULT.isSubtypeOf(expressionType, expectedType)) {
168                return expressionType;
169            }
170    
171            if (expression instanceof JetConstantExpression) {
172                CompileTimeConstant<?> value = ConstantExpressionEvaluator.object$.evaluate(expression, trace, expectedType);
173                if (value instanceof IntegerValueTypeConstant) {
174                    value = EvaluatePackage.createCompileTimeConstantWithType((IntegerValueTypeConstant) value, expectedType);
175                }
176                new CompileTimeConstantChecker(trace, true).checkConstantExpressionType(value, (JetConstantExpression) expression, expectedType);
177                return expressionType;
178            }
179    
180            DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, trace.getBindingContext());
181            for (JetType possibleType : dataFlowInfo.getPossibleTypes(dataFlowValue)) {
182                if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, expectedType)) {
183                    AutoCastUtils.recordCastOrError(expression, possibleType, trace, dataFlowValue.isStableIdentifier(), false);
184                    return possibleType;
185                }
186            }
187            trace.report(TYPE_MISMATCH.on(expression, expectedType, expressionType));
188            return expressionType;
189        }
190    
191        public static void recordExpectedType(@NotNull BindingTrace trace, @NotNull JetExpression expression, @NotNull JetType expectedType) {
192            if (expectedType != NO_EXPECTED_TYPE) {
193                JetType normalizeExpectedType = expectedType == UNIT_EXPECTED_TYPE ? KotlinBuiltIns.getInstance().getUnitType() : expectedType;
194                trace.record(BindingContext.EXPECTED_EXPRESSION_TYPE, expression, normalizeExpectedType);
195            }
196        }
197    
198        @NotNull
199        public static JetTypeInfo checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context, @NotNull DataFlowInfo dataFlowInfo) {
200            return JetTypeInfo.create(checkStatementType(expression, context), dataFlowInfo);
201        }
202    
203        @Nullable
204        public static JetType checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context) {
205            if (!noExpectedType(context.expectedType) && !KotlinBuiltIns.getInstance().isUnit(context.expectedType) && !context.expectedType.isError()) {
206                context.trace.report(EXPECTED_TYPE_MISMATCH.on(expression, context.expectedType));
207                return null;
208            }
209            return KotlinBuiltIns.getInstance().getUnitType();
210        }
211    
212        @NotNull
213        public static JetTypeInfo checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context, boolean isStatement, DataFlowInfo dataFlowInfo) {
214            return JetTypeInfo.create(checkImplicitCast(expressionType, expression, context, isStatement), dataFlowInfo);
215        }
216    
217        @Nullable
218        public static JetType checkImplicitCast(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull ExpressionTypingContext context, boolean isStatement) {
219            if (expressionType != null && context.expectedType == NO_EXPECTED_TYPE && context.contextDependency == INDEPENDENT && !isStatement
220                    && (KotlinBuiltIns.getInstance().isUnit(expressionType) || KotlinBuiltIns.getInstance().isAnyOrNullableAny(expressionType))) {
221                context.trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
222            }
223            return expressionType;
224        }
225    
226        @NotNull
227        public static JetTypeInfo illegalStatementType(@NotNull JetExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) {
228            facade.checkStatementType(
229                    expression, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT));
230            context.trace.report(EXPRESSION_EXPECTED.on(expression, expression));
231            return JetTypeInfo.create(null, context.dataFlowInfo);
232        }
233    }