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 DataFlowInfos infosForCondition = getDataFlowInfosForEntryCondition(
103 whenEntry, context.replaceDataFlowInfo(elseDataFlowInfo), subjectExpression, subjectType, subjectDataFlowValue);
104 elseDataFlowInfo = elseDataFlowInfo.and(infosForCondition.elseInfo);
105
106 JetExpression bodyExpression = whenEntry.getExpression();
107 if (bodyExpression != null) {
108 WritableScope scopeToExtend = newWritableScopeImpl(context, "Scope extended in when entry");
109 ExpressionTypingContext newContext = contextWithExpectedType
110 .replaceScope(scopeToExtend).replaceDataFlowInfo(infosForCondition.thenInfo).replaceContextDependency(INDEPENDENT);
111 CoercionStrategy coercionStrategy = isStatement ? CoercionStrategy.COERCION_TO_UNIT : CoercionStrategy.NO_COERCION;
112 JetTypeInfo typeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
113 scopeToExtend, Collections.singletonList(bodyExpression), coercionStrategy, newContext, context.trace);
114 JetType type = typeInfo.getType();
115 if (type != null) {
116 expressionTypes.add(type);
117 }
118 if (commonDataFlowInfo == null) {
119 commonDataFlowInfo = typeInfo.getDataFlowInfo();
120 }
121 else {
122 commonDataFlowInfo = commonDataFlowInfo.or(typeInfo.getDataFlowInfo());
123 }
124 }
125 }
126
127 if (commonDataFlowInfo == null) {
128 commonDataFlowInfo = context.dataFlowInfo;
129 }
130
131 if (!expressionTypes.isEmpty()) {
132 return DataFlowUtils.checkImplicitCast(CommonSupertypes.commonSupertype(expressionTypes), expression, contextWithExpectedType, isStatement, commonDataFlowInfo);
133 }
134 return JetTypeInfo.create(null, commonDataFlowInfo);
135 }
136
137 @NotNull
138 private DataFlowInfos getDataFlowInfosForEntryCondition(
139 @NotNull JetWhenEntry whenEntry,
140 @NotNull ExpressionTypingContext context,
141 @Nullable JetExpression subjectExpression,
142 @NotNull JetType subjectType,
143 @NotNull DataFlowValue subjectDataFlowValue
144 ) {
145 if (whenEntry.isElse()) {
146 return new DataFlowInfos(context.dataFlowInfo);
147 }
148
149 DataFlowInfos infos = null;
150 for (JetWhenCondition condition : whenEntry.getConditions()) {
151 DataFlowInfos conditionInfos = checkWhenCondition(subjectExpression, subjectExpression == null, subjectType, condition,
152 context, subjectDataFlowValue);
153 if (infos != null) {
154 infos = new DataFlowInfos(infos.thenInfo.or(conditionInfos.thenInfo), infos.elseInfo.and(conditionInfos.elseInfo));
155 }
156 else {
157 infos = conditionInfos;
158 }
159 }
160 return infos != null ? infos : new DataFlowInfos(context.dataFlowInfo);
161 }
162
163 private DataFlowInfos checkWhenCondition(
164 @Nullable final JetExpression subjectExpression,
165 final boolean expectedCondition,
166 final JetType subjectType,
167 JetWhenCondition condition,
168 final ExpressionTypingContext context,
169 final DataFlowValue subjectDataFlowValue
170 ) {
171 final Ref<DataFlowInfos> newDataFlowInfo = new Ref<DataFlowInfos>(noChange(context));
172 condition.accept(new JetVisitorVoid() {
173 @Override
174 public void visitWhenConditionInRange(@NotNull JetWhenConditionInRange condition) {
175 JetExpression rangeExpression = condition.getRangeExpression();
176 if (rangeExpression == null) return;
177 if (expectedCondition) {
178 context.trace.report(EXPECTED_CONDITION.on(condition));
179 DataFlowInfo dataFlowInfo = facade.getTypeInfo(rangeExpression, context).getDataFlowInfo();
180 newDataFlowInfo.set(new DataFlowInfos(dataFlowInfo, dataFlowInfo));
181 return;
182 }
183 JetTypeInfo typeInfo = facade.checkInExpression(condition, condition.getOperationReference(),
184 subjectExpression, rangeExpression, context);
185 DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo();
186 newDataFlowInfo.set(new DataFlowInfos(dataFlowInfo, dataFlowInfo));
187 if (!KotlinBuiltIns.getInstance().getBooleanType().equals(typeInfo.getType())) {
188 context.trace.report(TYPE_MISMATCH_IN_RANGE.on(condition));
189 }
190 }
191
192 @Override
193 public void visitWhenConditionIsPattern(@NotNull JetWhenConditionIsPattern condition) {
194 if (expectedCondition) {
195 context.trace.report(EXPECTED_CONDITION.on(condition));
196 }
197 if (condition.getTypeRef() != null) {
198 DataFlowInfos result = checkTypeForIs(context, subjectType, condition.getTypeRef(), subjectDataFlowValue);
199 if (condition.isNegated()) {
200 newDataFlowInfo.set(new DataFlowInfos(result.elseInfo, result.thenInfo));
201 }
202 else {
203 newDataFlowInfo.set(result);
204 }
205 }
206 }
207
208 @Override
209 public void visitWhenConditionWithExpression(@NotNull JetWhenConditionWithExpression condition) {
210 JetExpression expression = condition.getExpression();
211 if (expression != null) {
212 newDataFlowInfo.set(checkTypeForExpressionCondition(context, expression, subjectType, subjectExpression == null,
213 subjectDataFlowValue));
214 }
215 }
216
217 @Override
218 public void visitJetElement(@NotNull JetElement element) {
219 context.trace.report(UNSUPPORTED.on(element, getClass().getCanonicalName()));
220 }
221 });
222 return newDataFlowInfo.get();
223 }
224
225 private static class DataFlowInfos {
226 private final DataFlowInfo thenInfo;
227 private final DataFlowInfo elseInfo;
228
229 private DataFlowInfos(DataFlowInfo thenInfo, DataFlowInfo elseInfo) {
230 this.thenInfo = thenInfo;
231 this.elseInfo = elseInfo;
232 }
233
234 private DataFlowInfos(DataFlowInfo info) {
235 this(info, info);
236 }
237 }
238
239 private DataFlowInfos checkTypeForExpressionCondition(
240 ExpressionTypingContext context,
241 JetExpression expression,
242 JetType subjectType,
243 boolean conditionExpected,
244 DataFlowValue subjectDataFlowValue
245 ) {
246 if (expression == null) {
247 return noChange(context);
248 }
249 JetTypeInfo typeInfo = facade.getTypeInfo(expression, context);
250 JetType type = typeInfo.getType();
251 if (type == null) {
252 return noChange(context);
253 }
254 context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo());
255 if (conditionExpected) {
256 JetType booleanType = KotlinBuiltIns.getInstance().getBooleanType();
257 if (!JetTypeChecker.DEFAULT.equalTypes(booleanType, type)) {
258 context.trace.report(TYPE_MISMATCH_IN_CONDITION.on(expression, type));
259 }
260 else {
261 DataFlowInfo ifInfo = DataFlowUtils.extractDataFlowInfoFromCondition(expression, true, context);
262 DataFlowInfo elseInfo = DataFlowUtils.extractDataFlowInfoFromCondition(expression, false, context);
263 return new DataFlowInfos(ifInfo, elseInfo);
264 }
265 return noChange(context);
266 }
267 checkTypeCompatibility(context, type, subjectType, expression);
268 DataFlowValue expressionDataFlowValue =
269 DataFlowValueFactory.createDataFlowValue(expression, type, context.trace.getBindingContext());
270 DataFlowInfos result = noChange(context);
271 result = new DataFlowInfos(
272 result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue),
273 result.elseInfo.disequate(subjectDataFlowValue, expressionDataFlowValue)
274 );
275 return result;
276 }
277
278 private DataFlowInfos checkTypeForIs(
279 ExpressionTypingContext context,
280 JetType subjectType,
281 JetTypeReference typeReferenceAfterIs,
282 DataFlowValue subjectDataFlowValue
283 ) {
284 if (typeReferenceAfterIs == null) {
285 return noChange(context);
286 }
287 TypeResolutionContext typeResolutionContext = new TypeResolutionContext(context.scope, context.trace, true, /*allowBareTypes=*/ true);
288 PossiblyBareType possiblyBareTarget = components.expressionTypingServices.getTypeResolver().resolvePossiblyBareType(typeResolutionContext, typeReferenceAfterIs);
289 JetType type = TypeReconstructionUtil.reconstructBareType(typeReferenceAfterIs, possiblyBareTarget, subjectType, context.trace);
290 if (!subjectType.isNullable() && type.isNullable()) {
291 JetTypeElement element = typeReferenceAfterIs.getTypeElement();
292 assert element instanceof JetNullableType : "element must be instance of " + JetNullableType.class.getName();
293 JetNullableType nullableType = (JetNullableType) element;
294 context.trace.report(Errors.USELESS_NULLABLE_CHECK.on(nullableType));
295 }
296 checkTypeCompatibility(context, type, subjectType, typeReferenceAfterIs);
297 if (CastDiagnosticsUtil.isCastErased(subjectType, type, JetTypeChecker.DEFAULT)) {
298 context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, type));
299 }
300 return new DataFlowInfos(context.dataFlowInfo.establishSubtyping(subjectDataFlowValue, type), context.dataFlowInfo);
301 }
302
303 private static DataFlowInfos noChange(ExpressionTypingContext context) {
304 return new DataFlowInfos(context.dataFlowInfo, context.dataFlowInfo);
305 }
306
307 /*
308 * (a: SubjectType) is Type
309 */
310 private static void checkTypeCompatibility(
311 @NotNull ExpressionTypingContext context,
312 @Nullable JetType type,
313 @NotNull JetType subjectType,
314 @NotNull JetElement reportErrorOn
315 ) {
316 // TODO : Take auto casts into account?
317 if (type == null) {
318 return;
319 }
320 if (isIntersectionEmpty(type, subjectType)) {
321 context.trace.report(INCOMPATIBLE_TYPES.on(reportErrorOn, type, subjectType));
322 return;
323 }
324
325 // check if the pattern is essentially a 'null' expression
326 if (KotlinBuiltIns.getInstance().isNullableNothing(type) && !subjectType.isNullable()) {
327 context.trace.report(SENSELESS_NULL_IN_WHEN.on(reportErrorOn));
328 }
329 }
330 }