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