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.collect.Sets;
020 import com.intellij.openapi.util.Ref;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.jet.lang.diagnostics.Errors;
024 import org.jetbrains.jet.lang.psi.*;
025 import org.jetbrains.jet.lang.resolve.BindingContext;
026 import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
027 import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue;
028 import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
029 import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
030 import org.jetbrains.jet.lang.types.*;
031 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
032 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
033
034 import java.util.Collections;
035 import java.util.Set;
036
037 import static org.jetbrains.jet.lang.diagnostics.Errors.*;
038 import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.newWritableScopeImpl;
039
040 public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
041 protected PatternMatchingTypingVisitor(@NotNull ExpressionTypingInternals facade) {
042 super(facade);
043 }
044
045 @Override
046 public JetTypeInfo visitIsExpression(JetIsExpression expression, ExpressionTypingContext contextWithExpectedType) {
047 ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE);
048 JetExpression leftHandSide = expression.getLeftHandSide();
049 JetTypeInfo typeInfo = facade.safeGetTypeInfo(leftHandSide, context.replaceScope(context.scope));
050 JetType knownType = typeInfo.getType();
051 DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo();
052 if (expression.getTypeRef() != null) {
053 DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(leftHandSide, knownType, context.trace.getBindingContext());
054 DataFlowInfo conditionInfo = checkTypeForIs(context, knownType, expression.getTypeRef(), dataFlowValue).thenInfo;
055 DataFlowInfo newDataFlowInfo = conditionInfo.and(dataFlowInfo);
056 context.trace.record(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression, newDataFlowInfo);
057 }
058 return DataFlowUtils.checkType(KotlinBuiltIns.getInstance().getBooleanType(), expression, contextWithExpectedType, dataFlowInfo);
059 }
060
061 @Override
062 public JetTypeInfo visitWhenExpression(JetWhenExpression expression, ExpressionTypingContext context) {
063 return visitWhenExpression(expression, context, false);
064 }
065
066 public JetTypeInfo visitWhenExpression(JetWhenExpression expression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) {
067 ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE);
068 // TODO :change scope according to the bound value in the when header
069 JetExpression subjectExpression = expression.getSubjectExpression();
070
071 JetType subjectType;
072 if (subjectExpression == null) {
073 subjectType = ErrorUtils.createErrorType("Unknown type");
074 }
075 else {
076 JetTypeInfo typeInfo = facade.safeGetTypeInfo(subjectExpression, context);
077 subjectType = typeInfo.getType();
078 context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo());
079 }
080 DataFlowValue subjectDataFlowValue = subjectExpression != null
081 ? DataFlowValueFactory.INSTANCE.createDataFlowValue(subjectExpression, subjectType, context.trace.getBindingContext())
082 : DataFlowValue.NULL;
083
084 // TODO : exhaustive patterns
085
086 Set<JetType> expressionTypes = Sets.newHashSet();
087 DataFlowInfo commonDataFlowInfo = null;
088 DataFlowInfo elseDataFlowInfo = context.dataFlowInfo;
089 for (JetWhenEntry whenEntry : expression.getEntries()) {
090 JetWhenCondition[] conditions = whenEntry.getConditions();
091 DataFlowInfo newDataFlowInfo;
092 WritableScope scopeToExtend;
093 if (whenEntry.isElse()) {
094 scopeToExtend = newWritableScopeImpl(context, "Scope extended in when-else entry");
095 newDataFlowInfo = elseDataFlowInfo;
096 }
097 else if (conditions.length == 1) {
098 scopeToExtend = newWritableScopeImpl(context, "Scope extended in when entry");
099 newDataFlowInfo = context.dataFlowInfo;
100 JetWhenCondition condition = conditions[0];
101 if (condition != null) {
102 DataFlowInfos infos = checkWhenCondition(
103 subjectExpression, subjectExpression == null,
104 subjectType, condition,
105 context, subjectDataFlowValue);
106 newDataFlowInfo = infos.thenInfo;
107 elseDataFlowInfo = elseDataFlowInfo.and(infos.elseInfo);
108 }
109 }
110 else {
111 scopeToExtend = newWritableScopeImpl(context, "pattern matching"); // We don't write to this scope
112 newDataFlowInfo = null;
113 for (JetWhenCondition condition : conditions) {
114 DataFlowInfos infos = checkWhenCondition(subjectExpression, subjectExpression == null, subjectType, condition,
115 context, subjectDataFlowValue);
116 if (newDataFlowInfo == null) {
117 newDataFlowInfo = infos.thenInfo;
118 }
119 else {
120 newDataFlowInfo = newDataFlowInfo.or(infos.thenInfo);
121 }
122 elseDataFlowInfo = elseDataFlowInfo.and(infos.elseInfo);
123 }
124 if (newDataFlowInfo == null) {
125 newDataFlowInfo = context.dataFlowInfo;
126 }
127 }
128 JetExpression bodyExpression = whenEntry.getExpression();
129 if (bodyExpression != null) {
130 ExpressionTypingContext newContext = contextWithExpectedType.replaceScope(scopeToExtend).replaceDataFlowInfo(newDataFlowInfo);
131 CoercionStrategy coercionStrategy = isStatement ? CoercionStrategy.COERCION_TO_UNIT : CoercionStrategy.NO_COERCION;
132 JetTypeInfo typeInfo = context.expressionTypingServices.getBlockReturnedTypeWithWritableScope(scopeToExtend, Collections.singletonList(bodyExpression), coercionStrategy, newContext, context.trace);
133 JetType type = typeInfo.getType();
134 if (type != null) {
135 expressionTypes.add(type);
136 }
137 if (commonDataFlowInfo == null) {
138 commonDataFlowInfo = typeInfo.getDataFlowInfo();
139 }
140 else {
141 commonDataFlowInfo = commonDataFlowInfo.or(typeInfo.getDataFlowInfo());
142 }
143 }
144 }
145
146 if (commonDataFlowInfo == null) {
147 commonDataFlowInfo = context.dataFlowInfo;
148 }
149
150 if (!expressionTypes.isEmpty()) {
151 return DataFlowUtils.checkImplicitCast(CommonSupertypes.commonSupertype(expressionTypes), expression, contextWithExpectedType, isStatement, commonDataFlowInfo);
152 }
153 return JetTypeInfo.create(null, commonDataFlowInfo);
154 }
155
156 private DataFlowInfos checkWhenCondition(
157 @Nullable final JetExpression subjectExpression,
158 final boolean expectedCondition,
159 final JetType subjectType,
160 JetWhenCondition condition,
161 final ExpressionTypingContext context,
162 final DataFlowValue subjectDataFlowValue
163 ) {
164 final Ref<DataFlowInfos> newDataFlowInfo = new Ref<DataFlowInfos>(noChange(context));
165 condition.accept(new JetVisitorVoid() {
166 @Override
167 public void visitWhenConditionInRange(JetWhenConditionInRange condition) {
168 JetExpression rangeExpression = condition.getRangeExpression();
169 if (rangeExpression == null) return;
170 if (expectedCondition) {
171 context.trace.report(EXPECTED_CONDITION.on(condition));
172 DataFlowInfo dataFlowInfo = facade.getTypeInfo(rangeExpression, context).getDataFlowInfo();
173 newDataFlowInfo.set(new DataFlowInfos(dataFlowInfo, dataFlowInfo));
174 return;
175 }
176 JetTypeInfo typeInfo = facade.checkInExpression(condition, condition.getOperationReference(),
177 subjectExpression, rangeExpression, context);
178 DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo();
179 newDataFlowInfo.set(new DataFlowInfos(dataFlowInfo, dataFlowInfo));
180 if (!KotlinBuiltIns.getInstance().getBooleanType().equals(typeInfo.getType())) {
181 context.trace.report(TYPE_MISMATCH_IN_RANGE.on(condition));
182 }
183 }
184
185 @Override
186 public void visitWhenConditionIsPattern(JetWhenConditionIsPattern condition) {
187 if (expectedCondition) {
188 context.trace.report(EXPECTED_CONDITION.on(condition));
189 }
190 if (condition.getTypeRef() != null) {
191 DataFlowInfos result = checkTypeForIs(context, subjectType, condition.getTypeRef(), subjectDataFlowValue);
192 if (condition.isNegated()) {
193 newDataFlowInfo.set(new DataFlowInfos(result.elseInfo, result.thenInfo));
194 }
195 else {
196 newDataFlowInfo.set(result);
197 }
198 }
199 }
200
201 @Override
202 public void visitWhenConditionWithExpression(JetWhenConditionWithExpression condition) {
203 JetExpression expression = condition.getExpression();
204 if (expression != null) {
205 newDataFlowInfo.set(checkTypeForExpressionCondition(context, expression, subjectType, subjectExpression == null,
206 subjectDataFlowValue));
207 }
208 }
209
210 @Override
211 public void visitJetElement(JetElement element) {
212 context.trace.report(UNSUPPORTED.on(element, getClass().getCanonicalName()));
213 }
214 });
215 return newDataFlowInfo.get();
216 }
217
218 private static class DataFlowInfos {
219 private final DataFlowInfo thenInfo;
220 private final DataFlowInfo elseInfo;
221
222 private DataFlowInfos(DataFlowInfo thenInfo, DataFlowInfo elseInfo) {
223 this.thenInfo = thenInfo;
224 this.elseInfo = elseInfo;
225 }
226 }
227
228 private DataFlowInfos checkTypeForExpressionCondition(
229 ExpressionTypingContext context,
230 JetExpression expression,
231 JetType subjectType,
232 boolean conditionExpected,
233 DataFlowValue subjectDataFlowValue
234 ) {
235 if (expression == null) {
236 return noChange(context);
237 }
238 JetTypeInfo typeInfo = facade.getTypeInfo(expression, context);
239 JetType type = typeInfo.getType();
240 if (type == null) {
241 return noChange(context);
242 }
243 context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo());
244 if (conditionExpected) {
245 JetType booleanType = KotlinBuiltIns.getInstance().getBooleanType();
246 if (!JetTypeChecker.INSTANCE.equalTypes(booleanType, type)) {
247 context.trace.report(TYPE_MISMATCH_IN_CONDITION.on(expression, type));
248 }
249 else {
250 DataFlowInfo ifInfo = DataFlowUtils.extractDataFlowInfoFromCondition(expression, true, context);
251 DataFlowInfo elseInfo = DataFlowUtils.extractDataFlowInfoFromCondition(expression, false, context);
252 return new DataFlowInfos(ifInfo, elseInfo);
253 }
254 return noChange(context);
255 }
256 checkTypeCompatibility(context, type, subjectType, expression);
257 DataFlowValue expressionDataFlowValue =
258 DataFlowValueFactory.INSTANCE.createDataFlowValue(expression, type, context.trace.getBindingContext());
259 DataFlowInfos result = noChange(context);
260 result = new DataFlowInfos(
261 result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue),
262 result.elseInfo.disequate(subjectDataFlowValue, expressionDataFlowValue)
263 );
264 return result;
265 }
266
267 private static DataFlowInfos checkTypeForIs(
268 ExpressionTypingContext context,
269 JetType subjectType,
270 JetTypeReference typeReferenceAfterIs,
271 DataFlowValue subjectDataFlowValue
272 ) {
273 if (typeReferenceAfterIs == null) {
274 return noChange(context);
275 }
276 JetType type = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, typeReferenceAfterIs, context.trace, true);
277 if (!subjectType.isNullable() && type.isNullable()) {
278 JetTypeElement element = typeReferenceAfterIs.getTypeElement();
279 assert element instanceof JetNullableType : "element must be instance of " + JetNullableType.class.getName();
280 JetNullableType nullableType = (JetNullableType) element;
281 context.trace.report(Errors.USELESS_NULLABLE_CHECK.on(nullableType));
282 }
283 checkTypeCompatibility(context, type, subjectType, typeReferenceAfterIs);
284 if (BasicExpressionTypingVisitor.isCastErased(subjectType, type, JetTypeChecker.INSTANCE)) {
285 context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, type));
286 }
287 return new DataFlowInfos(context.dataFlowInfo.establishSubtyping(subjectDataFlowValue, type), context.dataFlowInfo);
288 }
289
290 private static DataFlowInfos noChange(ExpressionTypingContext context) {
291 return new DataFlowInfos(context.dataFlowInfo, context.dataFlowInfo);
292 }
293
294 /*
295 * (a: SubjectType) is Type
296 */
297 private static void checkTypeCompatibility(
298 @NotNull ExpressionTypingContext context,
299 @Nullable JetType type,
300 @NotNull JetType subjectType,
301 @NotNull JetElement reportErrorOn
302 ) {
303 // TODO : Take auto casts into account?
304 if (type == null) {
305 return;
306 }
307 if (TypeUtils.isIntersectionEmpty(type, subjectType)) {
308 context.trace.report(INCOMPATIBLE_TYPES.on(reportErrorOn, type, subjectType));
309 return;
310 }
311
312 // check if the pattern is essentially a 'null' expression
313 if (KotlinBuiltIns.getInstance().isNullableNothing(type) && !subjectType.isNullable()) {
314 context.trace.report(SENSELESS_NULL_IN_WHEN.on(reportErrorOn));
315 }
316 }
317 }