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