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.INSTANCE.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.INSTANCE.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 }